Compare commits

...

165 Commits

Author SHA1 Message Date
Steffen Eiden
03f7566436 New release s390-tools-2.44.0
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 15:51:50 +02:00
Marc Hartmayer
177d8ae0dd pvimg: Get rid of most 'ignore' doctest directives
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Marc Hartmayer
08d21b24b4 pvimg: Use indexed array for UV key hashes
Replace individual UV key hash fields with an indexed array and
introduce dedicated types for hash indices and key hash kinds. This
simplifies hash handling.

Co-developed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Marc Hartmayer
ed74e84862 pvimg: Report host-key document type mismatches
Report host-key document type mismatches before hash verification.

For example, when testing a v2 Secure Execution header with a classical
host-key document, report that a hybrid host-key document is required
instead of failing with a generic header validation error.

Before:

$ pvimg -- test --host-key-document v1.hkd se.v2.img
error: Invalid Secure Execution header

After:

$ pvimg -- test --host-key-document v1.hkd se.v2.img
Host-key document 'v1.hkd' is not a hybrid host-key document.
error: The provided host key document in v1.hkd contains the wrong number of keys!

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Marc Hartmayer
0263637d9f pv: Refactor 'get_verified_hkds'
Get rid of 'read_hkd' by refactoring the 'get_verified_hkds' function.
For this a new HkdLoader::load_and_verify is introduced that is a
reworked version of the original code.

In addition, add test cases for testing all the edge cases.

Assisted-by: IBM Bob:1.0.6
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Mete Durlu
1aa1558f91 zmemtopo: Add case-insensitive partition name filtering
Introduce the -p/--partition-filter option to display only partitions
whose names contain a specified substring. The filter performs
case-insensitive matching and applies consistently across all output
formats (table, tree, and reverse tree views).

Example:

$ zmemtopo -p "part74"
LPAR/LEVEL      SIZE
PARTITION74      12G
└LEVEL4_1        12G
  ├LEVEL3_0       3G
  ├LEVEL3_1       3G
  ├LEVEL3_2       3G
  └LEVEL3_3       3G

Suggested-by: Niklas Schnelle <schnelle@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Mete Durlu
29a1ef0023 zmemtopo: Fix table view spacing
Left justify partition number and partition name fields on table view
to better accommodate untrimmed and longer partition names.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Mete Durlu
39eb46763b zmemtopo: Remove false leading/trailing space trimming
Remove all util_strstrip() calls. The current users are command line
option stripping and partition name stripping.
Keep user passed arguments as is, typical shells already remove leading
and trailing whitespace unless explicitly quoted.
Spaces in partition names are valid. Keep the leading/trailing spaces
to preserve user given partition names.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Mete Durlu
ce1464ad5f zmemtopo: Clean up hardcoded value
Replace array length of lpar name with LPAR_NAME_LEN macro.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Steffen Eiden
c6d74eebfb pvimg: Fix hash comparison in SeHdrAadV2::contains_hash
Ensure both sides of the hash comparison use the same slice length
by explicitly slicing both the keyslot hash and the input hash to
UV_KEY_HASH_SIZE. Previously, only the keyslot hash was sliced while
comparing against the full input hash reference, which could lead to
incorrect comparisons if the input hash length differs.

This makes the comparison more explicit and ensures we're always
comparing equal-length hashes. This is useful if one hash is a subset of
another, e.g. a sha512 hash truncated to 32 bytes.

Fixes: 89577c2f8c ("pvimg: Use hybrid keys")
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-31 09:51:14 +02:00
Marc Hartmayer
ad129057b5 pvattest: tests: Implement TODO
Instead of blindly casting the values to u32 use try_into().unwrap().

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Marc Hartmayer
5b8fdc2bec pv: Improve API by marking some functions as must_use
Make the API less prone to error by specifying some of them as must_use.
This avoids issues that the result is not used.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Marc Hartmayer
784cff18c3 pv: Reuse RequestVersion and RequestMagic
Use the typedefs to make the intention and compatibility of the fields
more clear.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Timo Keller
fb33935c9b pvsecret: test using hybrid keys
Add unite tests and (binary) test asses for the hybrid key patch.

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Marc Hartmayer
175b336d32 rust/utils: Replace get_verified_hkds with get_verified_hkds_new
Now as no users of get_verified_hkds exists, replace it with
get_verified_hkds_new.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Steffen Eiden
9a5c9cd7f9 pv: Require matching versions of request and HKD
Enforce that a v{1,2} request also has a v{1,2} hostkey.
This requires to change the signature of Request::add_hostkey to return
a Result.

Co-Developed-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Steffen Eiden
73c227fa9a pvattest: Verify hybrid keys
Allow pvattest verify to verify hybrid keys. The hybrid key is
represented by a sha512 hash truncated to 32 bytes.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Steffen Eiden
1078e6d45f pvverify: Use hybrid keys
Allow pvverify to also verify hybrid keys. By default the key format is
autodetected.

Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Timo Keller
50808edb7c pvsecret: Use hybrid keys
Allow the creation of Add-secret requests using hybrid (=quantum safe)
keys. This results in using the headers in version 2 (0x200).

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:01 +02:00
Timo Keller
9dca2d3181 pvattest: Use hybrid keys
Allow the creation of Attestation requests using hybrid (=quantum safe)
keys. This results in using the headers in version 2 (0x200).

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Timo Keller
fc853f3259 pvimg/create-sehdr: Use hybrid keys
Allow the creation of SE headers with hybrid (=quantum safe) keys. This
results in using the headers in version 2 (0x200).

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Timo Keller
89577c2f8c pvimg: Use hybrid keys
Allow the creation of SE images using headers with hybrid (=quantum safe) keys.
This results in using the headers in version 2 (0x200).

Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
d6fc4921fb utils: Read hybrid HKDs
* Add `HkdVersion` (classic or hybrid) and `HkdVersionSelection`.
* Add `get_verified_hkds_new` that returns a list of verified HKDs
  read according to the given `HkdVersionSelection`.

Co-Developed-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
cb9c2f993c utils/cli: Introduce AutoOrExplicit
* Add `AutoOrExplicit` that dispatches between `Auto` and `Explicit`.
* Add `AutoOrExplicitParser` for it.

In a later commit, this will be used for auto detection of HKD version
or explicitly specifying the version.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
637c8196a6 pvimg: tests: Refactor test code and increase coverage
Refactor test code and increase coverage. The new test code tests for
the correct error kind instead of simply for an error.

Assisted-by: IBM Bob:1.0.6
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
c87d11abfe pvimg and create-sehdr: Refactor code to use generic flags
Replace version-specific flag enums (PcfV1, ScfV1) with a unified
SeHdrFlag enum and generic ControlFlagsModel. This refactoring provides:

 - Single SeHdrFlag enum for all control flags across SE header versions
 - Generic SeHdrControlFlagsModel with version-specific configurations
 - EffectiveControlFlags type replacing PlaintextControlFlagsV1/SecretControlFlagsV1
 - FlagsOverride mechanism for customizing flag configurations
 - Enhanced FromStr implementation for flag parsing
 - Comprehensive test coverage for flag operations and conversions

The new design improves maintainability by eliminating code duplication
and provides a more flexible API for working with SE header control
flags.

Assisted-by: IBM Bob:1.0.4
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
20fffb31d7 pvimg: Introduce generic flags
Add a generic flags framework for managing (Secure Execution) header
control flags. This implementation provides:

  - Clap compatibility for auto-completion and proper help messages
  - ControlFlagTrait for defining flag types with bit positions and version support
  - ControlFlagsModel for managing default flags and validation rules
  - FlagsOverride for customizing individual flag states
  - EffectiveControlFlags representing the final flag configuration
  - UnknownFlags handling for forward compatibility
  - Comprehensive error handling via FlagValidationError
  - Display implementations for both human-readable and binary formats
  - Type-safe conversions between flag representations

The framework supports version-aware flag validation, allowing flags to
be marked as supported only in specific Secure Execution header
versions. It enables flexible flag configuration through overrides while
maintaining type safety and validation.

Assisted-by: IBM Bob:1.0.4
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
2d330cd45f utils_macros: Implement derive_control_flag
Add a new derive macro 'derive_control_flag' that is used in the next
commit to reimplement how the code deals with Secure Execution control
flags.

It implements Display, IntoEnumIterator and the ControlFlagTrait for
enums using unit variants only.

  /// Trait for control flags that provide bit position information.
  pub trait ControlFlagTrait {
      /// Returns the bit position for this flag.
      fn bit_position(self) -> u8;
  }

Assisted-by: IBM Bob:1.0.4
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
618e22e38b utils: Add utils_macros: Implement ValueEnumDisplay and ValueEnumFromStr macros
Add new derive macros ValueEnumDisplay and ValueEnumFromStr which can be
used to derive 'Display' and 'FromStr' for enums implementing
'clap::ValueEnum'. It helps in reducing boilerplate code and keep things
in sync.

Assisted-by: IBM Bob:1.0.4
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
173a055372 pvimg: Use log level info for printing used flags
Use log level info for printed the used flags, instead of log level
debug.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Timo Keller
4670b108a6 pv: Implement HostKey::V2 and Keyslot::V2
Introduce HybridPublicKey combining ECDH and ML-KEM public keys. Use it
for HostKey::V2 and add Keyslot::V2 for hybrid keyslots.

Add tests for the new functionality.

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Timo Keller
ebe56592c8 pv: Add hybrid key derivation support
Add derive_aes256_gcm_key_hybrid() function that derives an Aes256GcmKey
and ML-KEM1024 ciphertext from a private ECDH customer key, a public
ECDH target key, and a public ML-KEM target key. This enables hybrid
post-quantum cryptography support.

Assisted-by: IBM Bob:1.0.5
Co-developed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Marc Hartmayer
1746d2bb6a pv: Change ec_key() to return Option<&PKeyRef<Public>>
Prepare the API for future host keys that might not have an EC key by
returning Option instead of a direct reference. Additionally, use
&PKeyRef<Public> as the borrowed view on PKey for better API
consistency.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2026-07-28 11:00:00 +02:00
Niklas Schnelle
91123e5d45 lib/zt_common.h: zpcimon: Add __force and use it to annotate le32toh() conversions
When using sparse on zpcimon several warnings like the one below are
generated:

  warning: incorrect type in argument 1 (different base types)
     expected unsigned int [usertype] __bsx
     got restricted __le32 [usertype] warning_temp_time

This is because several members in struct nvme_smart_log are marked as
__le32. These members are correctly converted to host endianness before
use via le32toh() respectively le16toh(). Since these functions take
their parameters as plain uint32_t or uint16_t however the implicit
conversion triggers the above warning. Fix this by adding the __force
attribute and using it to mark type conversions in the leXXtoh() calls.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
0f0e8cf33b zpcimon: Make optics_los_implemente() static
Sparse noted that optics_los_implemente() can be static. Make it so.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
7586a1ad59 zpcimon: Add missing include for __packed attribute
The __packed attribute is used without including the necessary
zt_common.h header, add it. Note that luckily the struct as it currently
exists doesn't actually required __packed as the natural alignment
leaves no holes. Still if in the future the reserved field is split up
it would need it and this keeps it consistent with the kernel
declaration.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
1da328921b dbginfo.sh: Let zpcimon log both optical module and SMART data
Add zpcimon data collection with both SMART log and optical module data.
Do so in the Z device subsystem commands section to account for the fact
that zpcimon is no longer network specific. At the same time keep the
existing opticsmon invocation for compatibility when executed on systems
without zpcimon.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Jörn Siglen <siglen@de.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
f96f09a5c9 nvmemon: libzpci: Skip SCLP on NVMes with non-IBM subsystem vendor ID
The SCLP Write Event Data Action Qualifier 4 is only available for
NVMes with subsystem vendor ID matching IBM. Add both IDs to struct
zpci_dev in libzpci. Also add them to the libzpci_example output
and adjust its output to multiline to handle more data.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
4fd607ae5c zpcimon: Monitor for hotplug of NVMes to trigger SMART collection
Use libudev to monitor for "change" events in the "nvme" subsystem that
use the "pcie" transport to trigger NVMe SMART data collection. Only
collect data from the NVMe referenced by the udev event.

Assisted-by: IBM Bob:1.0.1
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:51 +02:00
Niklas Schnelle
f1a11a01df zpcimon: Add JSON output for SMART data details
This uses the same format as:

   nvme smart-log --output-format=json /dev/nvmeX

One complication being that several values in the SMART data use
unsigned 128 bit integers. Both GCC and Clang support __uint128_t as
a C extension but don't offer printf() support for it. Just like
nvme-cli add a custom uint128_t to string function..

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
2eceedeb11 zpcimon: Add NVMe SMART data monitor
Add a new monitor which uses libnvme to collect SMART data from NVMes.
This monitor only implements the .collect_adapter_data operation since
it carries no state across data collections. Nevertheless for future
symmetry and possible future expansion also add an empty struct
nvmemon_ctx.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
a9dbb8a1a2 zpcimon: Factor out JSON printing of common PCI adapter attributes
Some of the attributes printed as JSON by opticsmon are common to all
PCI adapters factor these out into reusable helper functions
zpci_adapter_json_print_start() and zpci_adapter_json_print_end().

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
78dd82d129 libzpci: Extract get_device_node() from zpcictl to libzpci
The get_device_node() function for getting the name of an NVMe's
controller device e.g. "nvme0" as implemented in zpcictl is generally
useful and will be needed for NVMe SMART data collection in zpcimon as
well. Move it to libzpci and rename it to zpci_get_nvme_device_node() to
account for the change in namespace.

It would be tempting to pass a struct zpci_dev* instead of a const char*
but as zpcictl uses an incompatible struct zpci_device* this will
require a larger rework. Prioritize minimizing code duplication for now.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
113e9ebfef zpcimon: Split optics monitoring out into separate monitor
Introduce the concept of a generic zPCI device monitor by creating the
zpcimon_ops operations struct turning all optics specific calls into
abstract monitor calls. Handle monitors as a static array of zpcimon_ops
based monitors of which the now split out optics monitor is currently
the only one. All operations are in principle optional though a monitor
which provides an init operation must also provide the corresponding
destroy operation.

Keep the base64 based JSON pair as non optics specific for later re-use
and make it just skip the output in the very unlikely case that encoding
fails. In follow on commits a monitor for NVMe devices collecting SMART
data will be added.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
f9ef8a0106 util_time: zpcimon: Use a constant for seconds per day
Replace the magic value 86400 with a new util_time.h constant
SEC_PER_DAY.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
2c63c69af4 zpcimon: Allow setting output format and use JSON Lines in systemd unit
Introduce separate '--format' option to set output format. Use the new
JSON Lines format explicitly in the systemd unit. Since JSON Lines
format only uses printable characters this removes the need for passing
'-a' to systemctl status. At the same time JSON-SEQ is more easily
parsable using 'jq --seq' so keep it the default for easier scripting.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
8a4a4e0557 libutil/util_fmt: Add util_fmt_type_to_name()
Add a function to get the textual name of an enum util_fmt_t value. To
make this robust to changes in the order of elements in the format array
initialize this using named indices.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
e8550a4f8d opticsmon: zpcimon: Rename opticsmon to zpcimon
The opticsmon tool started out as a tool for monitoring the health of
optical modules in directly attached PCI NICs. In the future however it
will also monitor the health of other PCI devices. In particular in
a first step it will monitor the health of directly attached NVMe
devices.

To reflect this broadening of its scope rename opticsmon to zpcimon. Add
zpcimon.service and install it both under the new name and symlinked as
opticsmon.service for backwards compatibility. Since users are expected
to mostly just enable the service this keeps old instructions just
working.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
e7bc4a5d31 opticsmon: Enforce 1 second as minimal interval
So far it was possible for the user to specify an interval of 0 which
would result in not re-arming the timer at all thus disabling interval
based data collection. While this would still do optics data collection
on startup and link changes the intention is to collect data at least
every 24 hours. If interval is given as 0 treat this as minimum update
interval of 1 second.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
8b38bf37b0 opticsmon: Fix wrong size check for OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET
The code accesses OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET so the size needs
to be one larger for the LOS to be accessible.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
3696d6bd08 opticsmon: Handle error return of ethtool_nl_connect()
Don't just ignore the return of ethtool_nl_connect(). This would also
otherwise lead to invalid socket accesses later. Also make
ethtool_nl_connect() return negative error values instead of
EXIT_FAILURE.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
6847b6a8cf opticsmon: Fix error path free of struct optics in ethtool_nl_get_optics()
When ethtool_nl_get_optics() fails after ethtool_nl_cb() has allocated
oi->raw this memory would leak because only the struct optics* itself
was freed. Fix this by using optics_free() and relying on free(oi->raw)
being a no-op for NULL pointers in combination with using util_zalloc()
to handle errors both before and after oi->raw was allocated.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
1f339a9e7e opticsmon: Return errors from monitor_wait_loop() in monitor_mode()
Don't ignore the return value of monitor_wait_loop() but instead forward
it to the caller.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
905c3921af opticsmon: Close epoll fd in monitor_wait_loop()
The epoll fd (epfd) in monitor_wait_loop() is never closed. Close it in
all return paths. Also there is no explicit check for an error return of
epoll_create1() add that too.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Niklas Schnelle
e55e0e470a opticsmon: Fix wrong JSON print for tx_fault
The "tx_fault" field in the JSON output accidentally contained the
rx_los value instead of the tx_fault one. Change it to the correct
value. Note that this does not impact the data relayed to firmware as
that uses the raw module data.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-24 18:21:50 +02:00
Jan Höppner
9d9fca6e72 Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 18:49:20 +02:00
Jan Höppner
2a6657dfd1 New release s390-tools-2.43.1
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 18:46:13 +02:00
Hendrik Brueckner
b3dd05e16d iucvterm/ts-shell: Add Perl taint tests for auditdir and getpwuid()
The audit directory ts-shell configuration and also the output of
getpwuid() are considered tainted when running in Perl taint (-T) mode.

Provide untaint checks with validating the content.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

CCA key generation will fail is this case.

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

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

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

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

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

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

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

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

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

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

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

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

Return 0 in case of an error instead.

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

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

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

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

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

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

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

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

Fix this by first casting to uint32_t and then shift.

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

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

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

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

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

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

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

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

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

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

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

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

Add a regression test.

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

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

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

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

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

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

The redirect limit also prevents infinite redirect loops.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fix this by adding the necessary check.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Volkan Unal <vunal@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:01:58 +02:00
Jan Höppner
c66495888d Prepare for next release
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:49:06 +02:00
246 changed files with 14310 additions and 3348 deletions

8
.gitignore vendored
View File

@@ -102,9 +102,11 @@ lsstp/_lsstp
lsstp/lsstp.bash
mon_tools/mon_fsstatd
mon_tools/mon_procd
opticsmon/opticsmon
opticsmon/_opticsmon
opticsmon/opticsmon.bash
zpcimon/zpcimon
zpcimon/zpcimon.bash
zpcimon/_zpcimon
zpcimon/opticsmon.bash
zpcimon/_opticsmon
osasnmpd/osasnmpd
qetharp/qetharp
qethqoat/qethqoat

View File

@@ -1,6 +1,64 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.44.0 (2026-07-31)__
For Linux kernel version: 7.2
Changes of existing tools / libraries:
- create-sehdr: Enable quantum safe keys usage
- dbginfo.sh: Let zpcimon log both optical module and SMART data
- libutil/util_fmt: Add util_fmt_type_to_name()
- nvmemon: Skip SCLP on NVMes with non-IBM subsystem vendor ID
- opticsmon: zpcimon: Rename opticsmon to zpcimon
- pvattest: Enable quantum safe keys usage
- pvimg: Add '--flags <...>' and '--disable-flags <...>' option
- pvimg: Enable quantum safe keys usage
- pvsecret: Enable quantum safe keys usage
- pvverify: Enable quantum safe keys usage
- zmemtopo: Add CLI option to filter partitions by name
- zpcimon: Allow setting output format using --format CLI option
- zpcimon: Monitor for hotplug of NVMes to trigger SMART collection
Bug Fixes:
- opticsmon: Fix wrong size check for OPTICS_QSFP28_LOS_IMPLEMENTED_OFFSET
- opticsmon: Handle error return of ethtool_nl_connect()
- opticsmon: Fix error path free of struct optics in ethtool_nl_get_optics()
- opticsmon: Close epoll fd in monitor_wait_loop()
- opticsmon: Fix wrong JSON print for tx_fault
* __v2.43.1 (2026-07-22)__
For Linux kernel version: 7.1
Changes of existing tools:
- dasdfmt: Check disk type before prompting for blocksize
- iucvterm/ts-shell: Introduce config parameter to specify iucvconn binary
- iucvterm/ts-shell: Introduce pager config to replace env variable
- pv: Add root CA organization pinning to certificate verification
- zdump/ngdump: Use OpenSSL's SHA256 to compute digest of dump image
Bug Fixes:
- 95sel-ebc: Add udev-settle dependency
- 95sel-ebc: Add umount to boot service
- 95sel-ebc: Harden boot mount service
- fdasd: Fix memory leak in yes_no() function
- iucvterm/iucvtty: Ensure PTY and server fd's are closed at exec
- iucvterm/iucvtty: Prevent connection stalls when receiving TERM env
- iucvterm/iucvtty: Validate TERM environment name
- libkmipclient: Protect from symlink-following attacks
- libkmipclient: Various bugfixes
- libseckey: Protect from symlink-following attacks
- libseckey: Various bugfixes
- mon_procd: Fix possible static buffer overflow
- osasnmpd: Fix SNMP non-compliance
- zdev: Harden against invalid udev, import, firmware, or hypervisor data
- zipl-editenv: Check in-bootmap environment block validity
- zipl/boot: Check in-bootmap environment block syntax
- zipl: Check keyword duplications in BLS entries
- zipl: Fix bugs in mirror support
- zkey: Protect from symlink-following attacks
* __v2.43.0 (2026-06-25)__
For Linux kernel version: 7.1

View File

@@ -15,7 +15,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust opticsmon \
lsstp hsci hsavmcore chreipl-fcp-mpath ap_tools rust zpcimon \
zpwr zmemtopo
else

View File

@@ -316,7 +316,7 @@ build options:
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
| openssl | `HAVE_OPENSSL` | zkey, libekmfweb, libkmipclient, |
| | | zgetdump, rust/pvattest, rust/pvimg, |
| | | zgetdump/pvsecret, opticsmon |
| | | zgetdump/pvsecret, zpcimon |
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
| | | libkmipclient |
@@ -325,8 +325,9 @@ build options:
| | | rust/pvattest, rust/pvsecret, |
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
| systemd | `HAVE_SYSTEMD` | hsavmcore |
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
| libnl3 | `HAVE_LIBNL3` | opticsmon |
| libudev | `HAVE_LIBUDEV` | cpacfstatsd, zpcimon |
| libnl3 | `HAVE_LIBNL3` | zpcimon |
| libnvme | `HAVE_LIBNVME` | zpcimon |
This table lists additional build or install options:
@@ -374,11 +375,11 @@ the different tools are provided:
The runtime requirements are: openssl-libs (>= 1.1.1) and libcurl.
* opticsmon:
For building opticsmon OpenSSL and the Netlink Library Suite (libnl3) are
required.
Tip: you may skip the opticsmon build by adding
`HAVE_OPENSSL=0` or `HAVE_LIBNL3=0`
* zpcimon:
For building zpcimon OpenSSL, the Netlink Library Suite (libnl3),
and libnvme are required.
Tip: you may skip the zpcimon build by adding
`HAVE_OPENSSL=0` or `HAVE_LIBNL3=0` or `HAVE_LIBNVME=0`.
* osasnmpd:
You need at least the NET-SNMP 5.1.x package (net-snmp-devel.rpm)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,9 @@
#include "util_list.h"
#define PCI_BDF_LEN 13 /* DDDD:BB:dd.f\0 */
#define PCI_VENDOR_ID_IBM 0x1014
enum zpci_pft {
ZPCI_PFT_UNCLASSIFIED = 0x00,
@@ -65,6 +68,9 @@ struct zpci_dev {
bool uid_is_unique;
/* Configuration state 0 - Standby, 1 Configured */
bool conf;
/* Common PCI attributes */
uint16_t subsystem_vendor;
uint16_t subsystem_device;
/* Associated netdevs if any */
int num_netdevs;
@@ -96,4 +102,6 @@ operstate_t zpci_operstate_from_str(const char *oper_str);
struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
struct zpci_netdev **netdev);
char *zpci_get_nvme_device_node(const char *pci_addr);
#endif /* LIB_ZPCI_PCI_LIST_H */

View File

@@ -21,9 +21,11 @@
#define SCLP_ERRNOTIFY_AQ_DECONF 1
#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2
#define SCLP_ERRNOTIFY_AQ_OPTICS_DATA 3
#define SCLP_ERRNOTIFY_AQ_NVME_SMART_DATA 4
#define SCLP_ERRNOTIFY_ID_ZPCICTL 0x4713
#define SCLP_ERRNOTIFY_ID_OPTICSMON 0x4714
#define SCLP_ERRNOTIFY_ID_NVMEMON 0x4715
#define SCLP_ERRNOTIFY_DATA_SIZE 4054

View File

@@ -157,6 +157,16 @@ void util_fmt_exit(void);
*/
bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type);
/**
* util_fmt_type_to_name() - Get name of given format type identifier.
* @type: Format type identifier
*
* Get the name corresponding to the given format type identifier.
*
* Return: name of the format type
*/
const char *util_fmt_type_to_name(enum util_fmt_t type);
/**
* util_fmt_set_indent() - Set indentation parameters.
* @base : Base indentation level to apply to all output lines (default 0)

View File

@@ -19,6 +19,8 @@
#define MSEC_PER_SEC 1000L
#define SEC_PER_DAY (24 * 60 * 60)
/**
* Convert nanoseconds to microseconds
*

View File

@@ -72,6 +72,11 @@
#define __section(x) __attribute__((__section__(#x)))
#define __noinline __attribute__((__noinline__))
#define __big_endian
#ifdef __CHECKER__
# define __force __attribute__((force))
#else
# define __force
#endif
/* The Linux kernel (in stddef.h) and glibc (sys/cdefs.h) define
* __always_inline. Therefore undefine it first to allow the headers
* to be included first.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,9 +9,11 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
@@ -104,7 +106,7 @@ int decode_base64url(unsigned char *output, size_t *outlen,
len = EVP_DecodeBlock((unsigned char *)padded_output,
(unsigned char *)padded_input, padded_inlen);
if (len != (int)padded_inlen * 3 / 4) {
if ((size_t)len != padded_inlen * 3 / 4) {
rc = -EIO;
goto out;
}
@@ -364,9 +366,9 @@ out:
*payload_obj = pld;
else
json_object_put(pld);
if (signature != NULL && rc != 0) {
free(signature);
signature = NULL;
if (signature != NULL && *signature != NULL && rc != 0) {
free(*signature);
*signature = NULL;
*signature_len = 0;
}
if (json != NULL)
@@ -1035,7 +1037,7 @@ int clone_tag_def_list(const struct ekmf_tag_def_list *src,
if (src->tag_defs[i].description != NULL) {
dest->tag_defs[i].description =
strdup(src->tag_defs[i].description);
if (dest->tag_defs[i].description != NULL) {
if (dest->tag_defs[i].description == NULL) {
rc = -ENOMEM;
goto out;
}
@@ -1931,7 +1933,7 @@ int write_key_blob(const char *filename, unsigned char *key_blob,
if (filename == NULL || key_blob == NULL || key_blob_len == 0)
return -EINVAL;
fp = fopen(filename, "w");
fp = fopen_nofollow(filename, "w");
if (fp == NULL)
return -errno;
@@ -2052,7 +2054,7 @@ int write_x509_certificate(const char *pem_filename, X509 *cert)
if (pem_filename == NULL || cert == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2086,7 +2088,7 @@ int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr)
if (pem_filename == NULL || req == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2154,7 +2156,7 @@ int write_public_key(const char *pem_filename, EVP_PKEY *pkey)
if (pem_filename == NULL || pkey == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2606,3 +2608,33 @@ int json_object_object_add_ex(struct json_object *obj, const char *const key,
return 0;
}
#endif
FILE *fopen_nofollow(const char *path, const char *mode)
{
int flags = O_NOFOLLOW;
int fd;
FILE *fp;
/* Determine flags based on mode */
if (mode[0] == 'r')
flags |= (mode[1] == '+') ? O_RDWR : O_RDONLY;
else if (mode[0] == 'w')
flags |= O_CREAT | O_TRUNC |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else if (mode[0] == 'a')
flags |= O_CREAT | O_APPEND |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else
return NULL;
fd = open(path, flags, 0600);
if (fd < 0)
return NULL;
fp = fdopen(fd, mode);
if (fp == NULL) {
close(fd);
return NULL;
}
return fp;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -258,7 +258,7 @@ int kmip_node_add_structure_elements(struct kmip_node *node,
*
* @param node the KMIP node
*
* @returns the number of elements, or -1 if the node is not of type structure
* @returns the number of elements, or 0 if the node is not of type structure
*/
unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node)
{
@@ -266,10 +266,10 @@ unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node)
unsigned int i;
if (node == NULL)
return -1;
return 0;
if (node->type != KMIP_TYPE_STRUCTURE)
return -1;
return 0;
element = node->structure_value;
for (i = 0; element != NULL; i++)
@@ -319,7 +319,7 @@ struct kmip_node *kmip_node_get_structure_element_by_index(
* @param node the KMIP node
* @param tag the tag to find
*
* @returns the number of elements, or -1 if the node is not of type structure
* @returns the number of elements, or 0 if the node is not of type structure
*/
unsigned int kmip_node_get_structure_element_by_tag_count(
const struct kmip_node *node,
@@ -329,10 +329,10 @@ unsigned int kmip_node_get_structure_element_by_tag_count(
unsigned int i;
if (node == NULL)
return -1;
return 0;
if (node->type != KMIP_TYPE_STRUCTURE)
return -1;
return 0;
element = node->structure_value;
for (i = 0; element != NULL; element = element->next) {
@@ -897,7 +897,7 @@ struct kmip_node *kmip_node_clone(const struct kmip_node *node)
case KMIP_TYPE_TEXT_STRING:
if (node->text_value != NULL) {
clone->text_value = strdup(node->text_value);
if (node->text_value == NULL)
if (clone->text_value == NULL)
goto error;
clone->length = strlen(clone->text_value);
}
@@ -1489,7 +1489,7 @@ retry:
}
if (i == 0 && server_cert_pem != NULL) {
fp = fopen(server_cert_pem, "w");
fp = fopen_nofollow(server_cert_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s for write",
@@ -1507,7 +1507,7 @@ retry:
fp = NULL;
if (server_pubkey_pem != NULL) {
fp = fopen(server_pubkey_pem, "w");
fp = fopen_nofollow(server_pubkey_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s "
@@ -1533,7 +1533,7 @@ retry:
if (i > 0 && cert_chain_pem != NULL) {
if (fp == NULL)
fp = fopen(cert_chain_pem, "w");
fp = fopen_nofollow(cert_chain_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s for write",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -89,11 +89,26 @@ static const struct {
const char *name;
enum util_fmt_t fmt;
} formats[] = {
{ "json", FMT_JSON },
{ "json-seq", FMT_JSONSEQ },
{ "jsonl", FMT_JSONL },
{ "pairs", FMT_PAIRS },
{ "csv", FMT_CSV },
[FMT_JSON] = {
.name = "json",
.fmt = FMT_JSON
},
[FMT_JSONSEQ] = {
.name = "json-seq",
.fmt = FMT_JSONSEQ
},
[FMT_JSONL] = {
.name = "jsonl",
.fmt = FMT_JSONL
},
[FMT_PAIRS] = {
.name = "pairs",
.fmt = FMT_PAIRS
},
[FMT_CSV] = {
.name = "csv",
.fmt = FMT_CSV
},
};
/* Signal mask for blocking INT and TERM signals. */
@@ -112,6 +127,11 @@ bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type)
return false;
}
const char *util_fmt_type_to_name(enum util_fmt_t type)
{
return formats[type].name;
}
bool util_fmt_is_json(enum util_fmt_t type)
{
switch (type) {

View File

@@ -12,13 +12,17 @@ static void zpci_print(struct zpci_dev *zdev)
int i;
if (!zdev->conf) {
printf("fid: %8x address: %s\n", zdev->fid, pci_addr);
printf("fid:\t%4x address: %s\n", zdev->fid, pci_addr);
} else {
printf("fid: %8x address: %s uid: %4x%s pchid: %4x vfn: %4d port: %1d pft: %s ",
printf("fid:\t%4x address: %s uid: %4x%s pchid: %4x vfn: %4d\n",
zdev->fid, pci_addr, zdev->uid, (zdev->uid_is_unique) ? " (unique)" : "",
zdev->pchid, zdev->vfn, zdev->port, zpci_pft_str(zdev));
zdev->pchid, zdev->vfn);
printf("\n");
printf("\tport: %1d pft: %s subsystem_device: %4x subsystem_vendor: %4x\n",
zdev->port, zpci_pft_str(zdev), zdev->subsystem_device,
zdev->subsystem_vendor);
if (zdev->num_netdevs) {
printf("netdevs: ");
printf("\tnetdevs: ");
for (i = 0; i < zdev->num_netdevs; i++) {
printf("%s (%s)", zdev->netdevs[i].name,
zpci_operstate_str(zdev->netdevs[i].operstate));

View File

@@ -20,6 +20,7 @@
#include "lib/util_list.h"
#include "lib/util_path.h"
#include "lib/util_scandir.h"
#include "lib/util_sys.h"
/**
* Get the function type name for the given device
@@ -137,7 +138,7 @@ const char *zpci_operstate_str(operstate_t state)
static int zpci_populate_from_slot_dir(struct zpci_dev *zdev, const char *slot_dir,
const char *slot_name)
{
char buf_addr[11]; /* "dddd:bb:dd\0" */
char buf_addr[PCI_BDF_LEN];
uint8_t bus, df;
uint32_t domain;
int val, rc;
@@ -261,6 +262,16 @@ static int zpci_populate_from_dev_dir(struct zpci_dev *zdev)
goto out_path;
zdev->pft = val;
rc = util_file_read_i(&val, 16, "%s/subsystem_device", path);
if (rc)
goto out_path;
zdev->subsystem_device = val;
rc = util_file_read_i(&val, 16, "%s/subsystem_vendor", path);
if (rc)
goto out_path;
zdev->subsystem_vendor = val;
if (util_path_is_readable("%s/net", path)) {
rc = zpci_populate_netdevices(zdev, path);
if (rc)
@@ -387,3 +398,46 @@ struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_n
}
return NULL;
}
/**
* Get the NVMe device file name given a PCI address
*
* This function retrieves the NVMe device file "/dev/nvmeX"
* for a given PCI address. The device name can be used to construct
* the path /dev/nvmeX which is the NVMe's controller's character
* device used for example to retrieve S.M.A.R.T. data.
*
* @param[in] pci_addr The "DDDD:bb:dd.f" format PCI address
*
* @return The NVMe device file name if one is found NULL otherwise
*/
char *zpci_get_nvme_device_node(const char *pci_addr)
{
char *path, *dev = NULL;
char dev_addr[PCI_BDF_LEN];
struct dirent **de_vec;
int count, i;
path = util_path_sysfs("bus/pci/devices/%s/nvme", pci_addr);
count = util_scandir(&de_vec, alphasort, path, "nvme*");
if (count == -1) {
warnx("Could not read directory %s: %s", path, strerror(errno));
goto exit_path;
}
for (i = 0; i < count; i++) {
util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name);
if (util_sys_get_dev_addr(dev, dev_addr) != 0)
goto free_continue;
if (strcmp(dev_addr, pci_addr) == 0)
break;
free_continue:
free(dev);
dev = NULL;
}
util_scandir_free(de_vec, count);
exit_path:
free(path);
return dev;
}

View File

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

View File

@@ -1,407 +0,0 @@
/*
* opticsmon - Report optics monitoring data to firmware
*
* Copyright IBM Corp. 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <signal.h>
#include <sys/timerfd.h>
#include <time.h>
#include <linux/if.h>
#include "lib/util_list.h"
#include "lib/pci_list.h"
#include "lib/util_prg.h"
#include "lib/util_opt.h"
#include "lib/util_fmt.h"
#include "lib/util_libc.h"
#include <openssl/evp.h>
#include "ethtool.h"
#include "link_mon.h"
#include "optics_info.h"
#include "optics_sclp.h"
#include "opticsmon_cli.h"
#define API_LEVEL 1
struct options {
bool monitor;
bool report;
bool module_info;
bool quiet;
uint32_t interval_seconds;
};
struct opticsmon_ctx {
struct options opts;
struct ethtool_nl_ctx ethtool_ctx;
struct link_mon_nl_ctx lctx;
struct util_list *zpci_list;
};
static const struct util_prg prg = {
.desc = "Use opticsmon to monitor the health of the optical modules\n"
"of directly attached PCI based NICs",
.copyright_vec = { {
.owner = "IBM Corp.",
.pub_first = 2024,
.pub_last = 2024,
},
UTIL_PRG_COPYRIGHT_END }
};
static void parse_cmdline(int argc, char *argv[], struct options *opts)
{
uint32_t seconds;
int cmd, ret;
util_prg_init(&prg);
util_opt_init(opt_vec, NULL);
do {
cmd = util_opt_getopt_long(argc, argv);
switch (cmd) {
case 'm':
opts->monitor = true;
break;
case 'r':
opts->report = true;
break;
case 'q':
opts->quiet = true;
break;
case OPT_DUMP:
opts->module_info = true;
break;
case 'i':
ret = sscanf(optarg, "%u", &seconds);
if (ret != 1) {
fprintf(stderr,
"Failed to parse interval argument \"%s\" as seconds\n",
optarg);
exit(EXIT_FAILURE);
}
if (seconds < 86400)
opts->interval_seconds = seconds;
break;
case 'h':
util_prg_print_help();
util_opt_print_help();
exit(EXIT_SUCCESS);
case 'v':
util_prg_print_version();
exit(EXIT_SUCCESS);
case -1:
/* End of options string */
break;
}
} while (cmd != -1);
}
static int module_info_pair(struct optics *oi)
{
size_t b64_calclen, b64len;
int rc = EXIT_SUCCESS;
char *b64;
b64_calclen = (oi->size / 3) * 4;
if (oi->size % 3 > 0)
b64_calclen += 4;
b64 = util_zalloc(b64_calclen + 1); /* adds NUL byte */
b64len = EVP_EncodeBlock((unsigned char *)b64, oi->raw, oi->size);
if (b64len != b64_calclen) {
fprintf(stderr, "encoding base64 via openssl failed\n");
rc = EXIT_FAILURE;
goto out;
}
util_fmt_pair(FMT_QUOTE, "module_info", b64);
out:
free(b64);
return rc;
}
static void optics_json_print(struct opticsmon_ctx *ctx, struct zpci_netdev *nd, struct optics *oi)
{
util_fmt_obj_start(FMT_DEFAULT, "netdev");
util_fmt_pair(FMT_QUOTE, "name", nd->name);
util_fmt_pair(FMT_QUOTE, "operstate", zpci_operstate_str(nd->operstate));
util_fmt_obj_start(FMT_DEFAULT, "optics");
util_fmt_pair(FMT_QUOTE, "type", optics_type_str(optics_type(oi)));
util_fmt_pair(FMT_QUOTE, "rx_los", optics_los_str(optics_rx_los(oi)));
util_fmt_pair(FMT_QUOTE, "tx_los", optics_los_str(optics_tx_los(oi)));
util_fmt_pair(FMT_QUOTE, "tx_fault", optics_los_str(optics_rx_los(oi)));
if (ctx->opts.module_info)
module_info_pair(oi);
util_fmt_obj_end();
util_fmt_obj_end();
}
static int dump_adapter_data(struct opticsmon_ctx *ctx, struct zpci_dev *zdev)
{
struct optics **ois;
int num_ois = 0;
char *pci_addr;
int i, rc;
ois = util_zalloc(sizeof(ois[0]) * zdev->num_netdevs);
for (i = 0; i < zdev->num_netdevs; i++) {
rc = ethtool_nl_get_optics(&ctx->ethtool_ctx, zdev->netdevs[i].name, &ois[i]);
if (rc)
goto free_ois;
num_ois++;
}
if (!ctx->opts.quiet) {
util_fmt_obj_start(FMT_DEFAULT, "adapter");
util_fmt_pair(FMT_QUOTE, "pft", zpci_pft_str(zdev));
util_fmt_obj_start(FMT_DEFAULT, "ids");
util_fmt_pair(FMT_QUOTE, "fid", "0x%0x", zdev->fid);
if (zdev->uid_is_unique)
util_fmt_pair(FMT_QUOTE, "uid", "0x%0x", zdev->uid);
pci_addr = zpci_pci_addr(zdev);
util_fmt_pair(FMT_QUOTE, "pci_address", pci_addr);
free(pci_addr);
util_fmt_obj_end();
util_fmt_obj_start(FMT_LIST, "netdevs");
for (i = 0; i < zdev->num_netdevs; i++)
optics_json_print(ctx, &zdev->netdevs[i], ois[i]);
util_fmt_obj_end(); /* netdevs list */
util_fmt_obj_end(); /* adapter */
fflush(stdout);
}
if (ctx->opts.report) {
for (i = 0; i < zdev->num_netdevs; i++) {
rc = sclp_issue_optics_report(zdev, ois[i]);
if (rc == -ENOTSUP) {
fprintf(stderr, "Skipping %s which does not support reporting\n",
zdev->netdevs[i].name);
} else if (rc < 0) {
fprintf(stderr, "Error issuing SCLP for optics data failed: %s\n",
strerror(-rc));
}
}
}
free_ois:
for (i = 0; i < num_ois; i++)
optics_free(ois[i]);
free(ois);
return rc;
}
static void zpci_list_reload(struct util_list **zpci_list)
{
if (*zpci_list)
zpci_free_dev_list(*zpci_list);
*zpci_list = zpci_dev_list();
}
static void dump_all_adapter_data(struct opticsmon_ctx *ctx)
{
struct zpci_dev *zdev;
zpci_list_reload(&ctx->zpci_list);
util_list_iterate(ctx->zpci_list, zdev) {
/* Filter non-NIC devices and VFs */
if (zpci_is_vf(zdev) || !zdev->num_netdevs)
continue;
dump_adapter_data(ctx, zdev);
}
}
static int oneshot_mode(struct opticsmon_ctx *ctx)
{
util_fmt_init(stdout, FMT_JSON, FMT_DEFAULT, API_LEVEL);
if (!ctx->opts.quiet)
util_fmt_obj_start(FMT_LIST, "adapters");
dump_all_adapter_data(ctx);
if (!ctx->opts.quiet)
util_fmt_obj_end();
util_fmt_exit();
return EXIT_SUCCESS;
}
void on_link_change(struct zpci_netdev *netdev, void *arg)
{
struct opticsmon_ctx *ctx = arg;
struct zpci_netdev *found_netdev;
struct zpci_dev *zdev = NULL;
int reloads = 1;
do {
if (ctx->zpci_list) {
zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
if (zdev) {
/* Skip data collection if operational state is
* unchanged
*/
if (found_netdev->operstate == netdev->operstate)
return;
/* Update operation state for VFs even though
* they are skipped just for a consistent view
*/
found_netdev->operstate = netdev->operstate;
/* Only collect optics data for PFs */
if (!zpci_is_vf(zdev))
dump_adapter_data(ctx, zdev);
return;
}
}
/* Could be uninitalized list or a new device, retry after reload */
zpci_list_reload(&ctx->zpci_list);
reloads--;
} while (reloads > 0);
}
#define MAX_EVENTS 8
static int monitor_wait_loop(struct opticsmon_ctx *ctx, int sigfd, int timerfd)
{
struct epoll_event events[MAX_EVENTS];
struct signalfd_siginfo fdsi;
int i, nlfd, epfd, nfds;
struct epoll_event ev;
uint64_t expirations;
ssize_t sread;
epfd = epoll_create1(EPOLL_CLOEXEC);
ev.events = EPOLLIN;
ev.data.fd = sigfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &ev) == -1)
return -EIO;
ev.events = EPOLLIN;
ev.data.fd = timerfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, timerfd, &ev) == -1)
return -EIO;
nlfd = link_mon_nl_waitfd_getfd(&ctx->lctx);
ev.events = EPOLLIN;
ev.data.fd = nlfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, nlfd, &ev) == -1)
return -EIO;
while (1) {
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nfds < 0)
return nfds;
for (i = 0; i < nfds; i++) {
/* signal fd */
if (events[i].data.fd == sigfd) {
sread = read(sigfd, &fdsi, sizeof(fdsi));
if (sread != sizeof(fdsi))
return -EIO;
switch (fdsi.ssi_signo) {
case SIGINT:
case SIGTERM:
case SIGQUIT:
return 0;
/* Unexpected signal */
default:
return -EIO;
}
/* timer fd */
} else if (events[i].data.fd == timerfd) {
sread = read(timerfd, &expirations, sizeof(uint64_t));
if (sread != sizeof(uint64_t))
return -EIO;
if (!expirations)
continue;
dump_all_adapter_data(ctx);
/* netlink fd */
} else if (events[i].data.fd == nlfd) {
link_mon_nl_waitfd_read(&ctx->lctx);
}
}
}
return 0;
}
static int monitor_mode(struct opticsmon_ctx *ctx)
{
struct itimerspec timerspec;
int sigfd, timerfd, ret;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
sigaddset(&mask, SIGTERM);
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
return -EIO;
sigfd = signalfd(-1, &mask, 0);
if (sigfd == -1) {
fprintf(stderr, "Failed to create signalfd\n");
return -EIO;
}
timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timerfd == -1) {
fprintf(stderr, "Failed to create timerfd\n");
ret = -EIO;
goto close_signalfd;
}
/* Set initial expiration to 1 ns so we gather optics data at startup */
timerspec.it_value.tv_sec = 0;
timerspec.it_value.tv_nsec = 1;
timerspec.it_interval.tv_sec = ctx->opts.interval_seconds;
timerspec.it_interval.tv_nsec = 0;
ret = timerfd_settime(timerfd, 0, &timerspec, NULL);
if (ret == -1) {
fprintf(stderr, "Failed to arm timer\n");
goto close_timerfd;
}
util_fmt_init(stdout, FMT_JSONSEQ, FMT_DEFAULT, API_LEVEL);
ret = link_mon_nl_waitfd_create(&ctx->lctx, on_link_change, ctx);
if (ret) {
fprintf(stderr, "Failed to create link monitoring socket\n");
goto close_timerfd;
}
monitor_wait_loop(ctx, sigfd, timerfd);
link_mon_nl_waitfd_destroy(&ctx->lctx);
util_fmt_exit();
close_signalfd:
close(sigfd);
close_timerfd:
close(timerfd);
return ret;
}
int main(int argc, char **argv)
{
struct opticsmon_ctx ctx = { .opts = { .interval_seconds = 86400 } };
int ret;
parse_cmdline(argc, argv, &ctx.opts);
ethtool_nl_connect(&ctx.ethtool_ctx);
if (ctx.opts.monitor)
ret = monitor_mode(&ctx);
else
ret = oneshot_mode(&ctx);
ethtool_nl_close(&ctx.ethtool_ctx);
if (ctx.zpci_list)
zpci_free_dev_list(ctx.zpci_list);
return ret;
}

View File

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

View File

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

30
rust/Cargo.lock generated
View File

@@ -367,7 +367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -917,7 +917,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -958,6 +958,7 @@ dependencies = [
"s390_pv_core",
"serde",
"serde_test",
"testing_logger",
"thiserror",
"zerocopy",
]
@@ -1118,7 +1119,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -1128,7 +1129,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
dependencies = [
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
name = "testing_logger"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d92b727cb45d33ae956f7f46b966b25f1bc712092aeef9dba5ac798fc89f720"
dependencies = [
"log",
]
[[package]]
@@ -1212,9 +1222,21 @@ dependencies = [
"clap",
"libc",
"log",
"openssl",
"s390_pv",
"serde",
"serde_json",
"utils_macros",
]
[[package]]
name = "utils_macros"
version = "0.12.0"
dependencies = [
"clap",
"proc-macro2",
"quote",
"syn",
]
[[package]]

View File

@@ -11,6 +11,7 @@ members = [
"pvsecret",
"pvverify",
"utils",
"utils_macros",
]
resolver = "2"

View File

@@ -29,3 +29,4 @@ pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
[dev-dependencies]
serde_test = "1.0.177"
testing_logger = "0.1.1"

View File

@@ -17,6 +17,8 @@ use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGE
pub enum SeHdrVersion {
/// Secure Execution header v1
One = 0x100,
/// Secure Execution header v2
Two = 0x200,
}
/// Struct containing all SE-header tags.
@@ -241,6 +243,7 @@ impl BootHdrTags {
// Some sanity checks
let hdr_version = match hdr_head.version.get() {
0x100 => SeHdrVersion::One,
0x200 => SeHdrVersion::Two,
_ => {
debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get());
return Err(Error::InvBootHdr);

View File

@@ -7,12 +7,14 @@ use std::fmt::Display;
use std::ops::Range;
use enum_dispatch::enum_dispatch;
use openssl::bn::BigNumContext;
use openssl::derive::Deriver;
use openssl::ec::{EcGroup, EcKey};
use openssl::ec::{EcGroup, EcKey, EcPoint};
use openssl::error::ErrorStack;
use openssl::hash::{DigestBytes, MessageDigest};
use openssl::md::MdRef;
use openssl::nid::Nid;
use openssl::pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public};
use openssl::pkey::{HasPublic, Id, KeyType, PKey, PKeyRef, Private, Public};
use openssl::pkey_ctx::{HkdfMode, PkeyCtx};
use openssl::rand::rand_bytes;
use openssl::rsa::Padding;
@@ -23,8 +25,51 @@ use openssl::symm::{
use pv_core::request::Confidential;
use crate::error::Result;
use crate::openssl_extensions::PkeyEncapsulateContext;
use crate::req::get_pub_ecdh_points;
use crate::request::EcPubKeyCoord;
use crate::Error;
/// Compute ECDH shared secret from public and private keys
///
/// It is expected that the public and private key are with respect to EC-P521.
///
/// Note that the output is the concatenation of the 80-bytes-left-padded x and
/// 80-bytes-left-padded y coordinate.
fn ecdh_shared_secret(
pub_key: &PKeyRef<Public>,
priv_key: &PKeyRef<Private>,
) -> Result<[u8; 160], ErrorStack> {
let pub_key = pub_key.ec_key()?;
let priv_key = priv_key.ec_key()?;
// Verify both keys use the EC-P521 curve (SECP521R1)
assert_eq!(
pub_key.group().curve_name(),
Some(Nid::SECP521R1),
"Public key must use EC-P521 curve"
);
assert_eq!(
priv_key.group().curve_name(),
Some(Nid::SECP521R1),
"Private key must use EC-P521 curve"
);
pub_key.check_key()?;
priv_key.check_key()?;
let group = pub_key.group();
let mut bn_ctx = BigNumContext::new()?;
let mut point = EcPoint::new(group)?;
point.mul2(
group,
pub_key.public_key(),
priv_key.private_key(),
&mut bn_ctx,
)?;
let coord = get_pub_ecdh_points(&point, group)?;
Ok(coord)
}
/// 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]>;
/// An AES256-XTS key that will purge itself out of the memory when going out of scope
@@ -74,6 +119,7 @@ impl SymKeyType {
}
/// Returns true if the [`SymKeyType`] is an AEAD key
#[must_use]
pub const fn is_aead(&self) -> bool {
self.tag_len().is_some()
}
@@ -214,6 +260,220 @@ pub fn derive_aes256_gcm_key(k1: &PKeyRef<Private>, k2: &PKeyRef<Public>) -> Res
))
}
/// Determines the KeyType of a given PKey by testing against all known key types.
///
/// This function iterates through all known OpenSSL key types and uses the `is_a()`
/// method to identify which type the provided key matches. This is more reliable than
/// using `Id` to `KeyType` conversion, especially for newer key types like ML-KEM
/// that may not have a direct `Id` mapping.
///
/// # Parameters
///
/// * `key` - A reference to the PKey to identify
///
/// # Returns
///
/// * `Some(KeyType)` - If the key matches one of the known key types
/// * `None` - If the key type is not recognized or doesn't match any known types
fn pkey_to_keytype<T>(key: &PKeyRef<T>) -> Option<KeyType> {
const KNOWN_KEY_TYPES: &[KeyType] = &[
KeyType::RSA,
KeyType::RSA_PSS,
KeyType::DSA,
KeyType::DH,
KeyType::EC,
KeyType::HMAC,
KeyType::CMAC,
KeyType::X25519,
KeyType::ED25519,
KeyType::X448,
KeyType::ED448,
KeyType::ML_KEM_512,
KeyType::ML_KEM_768,
KeyType::ML_KEM_1024,
];
KNOWN_KEY_TYPES
.iter()
.find(|&&key_type| key.is_a(key_type))
.copied()
}
fn key_type_str(t: KeyType) -> &'static str {
if t == KeyType::RSA {
"RSA"
} else if t == KeyType::RSA_PSS {
"RSA-PSS"
} else if t == KeyType::DSA {
"DSA"
} else if t == KeyType::DH {
"DH"
} else if t == KeyType::EC {
"EC"
} else if t == KeyType::HMAC {
"HMAC"
} else if t == KeyType::CMAC {
"CMAC"
} else if t == KeyType::X25519 {
"X25519"
} else if t == KeyType::ED25519 {
"ED25519"
} else if t == KeyType::X448 {
"X448"
} else if t == KeyType::ED448 {
"ED448"
} else if t == KeyType::ML_KEM_512 {
"ML-KEM-512"
} else if t == KeyType::ML_KEM_768 {
"ML-KEM-768"
} else if t == KeyType::ML_KEM_1024 {
"ML-KEM-1024"
} else {
"unknown"
}
}
/// Validates that a key matches the expected key type.
///
/// # Errors
///
/// Returns an error if the key doesn't match the expected type.
pub(crate) fn validate_key_type<T: HasPublic>(
key: &PKeyRef<T>,
key_name: &str,
expected_type: KeyType,
) -> Result<()> {
if !key.is_a(expected_type) {
return Err(Error::RetrInvKey {
what: "key type",
kind: key_name.to_string(),
value: pkey_to_keytype(key)
.map(key_type_str)
.unwrap_or("unknown")
.to_string(),
exp: key_type_str(expected_type).to_string(),
});
}
Ok(())
}
/// Validates that a key is an EC key with the specified curve.
///
/// # Errors
///
/// Returns an error if the key is not an EC key or doesn't use the expected curve.
pub(crate) fn validate_ec_key<T: HasPublic>(
key: &PKeyRef<T>,
key_name: &str,
expected_curve: Nid,
) -> Result<()> {
if key.id() != Id::EC {
return Err(Error::RetrInvKey {
what: "key type",
kind: key_name.to_string(),
value: pkey_to_keytype(key)
.map(key_type_str)
.unwrap_or("unknown")
.to_string(),
exp: format!("EC ({})", expected_curve.long_name().unwrap_or("unknown")),
});
}
let ec_key = key.ec_key()?;
if ec_key.group().curve_name() != Some(expected_curve) {
return Err(Error::RetrInvKey {
what: "curve",
kind: key_name.to_string(),
value: ec_key
.group()
.curve_name()
.and_then(|nid| nid.long_name().ok())
.unwrap_or("unknown")
.to_string(),
exp: expected_curve.long_name().unwrap_or("unknown").to_string(),
});
}
Ok(())
}
/// Derive a symmetric AES 256 GCM key from a private target key, a public
/// customer key, and a public ML-KEM target key.
///
/// # Returns
///
/// The derived key and the ML-KEM ciphertext (KC).
///
/// # Errors
///
/// This function will return an error if something went bad in OpenSSL or the
/// wrong key types were used.
pub fn derive_aes256_gcm_key_hybrid(
priv_ecdh_cust_key: &PKeyRef<Private>,
pub_ecdh_target_key: &PKeyRef<Public>,
pub_mlkem_target_key: &PKeyRef<Public>,
) -> Result<(Aes256GcmKey, Vec<u8>)> {
let mut buffer: Vec<u8> = vec![0, 0, 0, 1];
validate_ec_key(priv_ecdh_cust_key, "ECDH customer key", Nid::SECP521R1)?;
validate_ec_key(pub_ecdh_target_key, "ECDH target key", Nid::SECP521R1)?;
validate_key_type(
pub_mlkem_target_key,
"ML-KEM target key",
KeyType::ML_KEM_1024,
)?;
// Derive the ECDH shared secret
let ecdh_derived_secret = ecdh_shared_secret(pub_ecdh_target_key, priv_ecdh_cust_key)?;
assert_eq!(ecdh_derived_secret.as_ref().len(), 160);
buffer.extend_from_slice(ecdh_derived_secret.as_ref());
// Derive the ML-KEM shared secret
let mut ctx = PkeyCtx::new(pub_mlkem_target_key)?;
ctx.encapsulate_init()?;
let (mut ciphertext, mut shared_secret) = (vec![], vec![]);
ctx.encapsulate_to_vec(&mut ciphertext, &mut shared_secret)?;
assert_eq!(ciphertext.len(), 1568);
assert_eq!(shared_secret.len(), 32);
buffer.extend_from_slice(&shared_secret);
// Append the private ECDH customer key
let pub_ecdh_cust_key = EcPubKeyCoord::try_from(priv_ecdh_cust_key)?;
assert_eq!(pub_ecdh_cust_key.as_ref().len(), 160);
buffer.extend_from_slice(pub_ecdh_cust_key.as_ref());
// Append the ciphertext
buffer.extend_from_slice(&ciphertext);
// Append the public ECDH target key
let pub_ecdh_target_key: EcPubKeyCoord = pub_ecdh_target_key.try_into()?;
assert_eq!(pub_ecdh_target_key.as_ref().len(), 160);
buffer.extend_from_slice(pub_ecdh_target_key.as_ref());
// Append the public ML-KEM target key
assert_eq!(pub_mlkem_target_key.raw_public_key()?.len(), 1568);
buffer.extend_from_slice(&pub_mlkem_target_key.raw_public_key()?);
// Append the magic string
const STRING: &str = "PQC Secure Execution with Format-2 Key Slots KS2";
assert_eq!(STRING.len(), 48);
buffer.extend_from_slice(STRING.as_bytes());
// Sanity check
assert_eq!(buffer.len(), 4 + 160 + 32 + 160 + 1568 + 160 + 1568 + 48);
let secr = Confidential::new(buffer);
// Panic: does not panic as SHA256 digest is 32 bytes long
Ok((
Aes256GcmKey::new(
hash(MessageDigest::sha256(), secr.value())?
.as_ref()
.try_into()
.unwrap(),
),
ciphertext,
))
}
/// Generate a random array.
///
/// # Errors
@@ -548,10 +808,167 @@ pub(crate) fn verify_signature<T: HasPublic>(
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::thread;
use super::*;
use crate::test_utils::*;
use crate::{get_test_asset, PvCoreError};
/// Test that deterministic RNG contexts are thread-local and don't interfere.
///
/// Per OpenSSL documentation (RAND_get0_primary(3)):
/// "The public and private DRBG are thread-local instances, which are used by
/// RAND_bytes() and RAND_priv_bytes(), respectively."
///
/// Reference: <https://docs.openssl.org/3.1/man3/RAND_get0_primary/>
///
/// Note: RAND_set0_public() and RAND_set0_private() require OpenSSL >= 3.1.
#[test]
fn test_deterministic_rng_thread_isolation() {
use std::sync::Barrier;
use openssl::rand::rand_bytes;
// Barriers to synchronize: thread1 installs → thread2 installs → both generate → both
// complete
let barrier_after_t1_install = Arc::new(Barrier::new(2));
let barrier_after_t2_install = Arc::new(Barrier::new(2));
let barrier_after_rand_bytes = Arc::new(Barrier::new(2));
let barrier1_clone = Arc::clone(&barrier_after_t1_install);
let barrier2_clone = Arc::clone(&barrier_after_t2_install);
let barrier3_clone = Arc::clone(&barrier_after_rand_bytes);
// Thread 1: Install deterministic RNG with specific entropy
let thread1 = thread::spawn(move || {
let entropy = [0x42u8; 4096];
let nonce = [0x24u8; 48];
// Install thread-local deterministic RNG
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Signal thread2 that we've installed our RNG
barrier1_clone.wait();
// Wait for thread2 to install its RNG
barrier2_clone.wait();
// Now generate bytes while thread2 also has its RNG installed
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Wait for thread2 to also complete rand_bytes
barrier3_clone.wait();
buf
});
// Thread 2: Install different deterministic RNG after thread1
let thread2 = thread::spawn(move || {
// Wait for thread1 to install its RNG first
barrier_after_t1_install.wait();
// Now install our own thread-local deterministic RNG with different entropy
let entropy = [0xAAu8; 4096];
let nonce = [0x55u8; 48];
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Signal thread1 that we've installed our RNG
barrier_after_t2_install.wait();
// Generate bytes with our different entropy (concurrently with thread1)
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Wait for thread1 to also complete rand_bytes
barrier_after_rand_bytes.wait();
buf
});
let t1_buf = thread1.join().unwrap();
let t2_buf = thread2.join().unwrap();
// Expected deterministic values for thread 1 (entropy=0x42, nonce=0x24)
let expected_t1: [u8; 32] = [
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
];
// Expected deterministic values for thread 2 (entropy=0xAA, nonce=0x55)
let expected_t2: [u8; 32] = [
170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
];
// Verify each thread produced its expected deterministic output
assert_eq!(
t1_buf, expected_t1,
"Thread 1 should produce deterministic output"
);
assert_eq!(
t2_buf, expected_t2,
"Thread 2 should produce deterministic output"
);
// Also verify they are different (proves thread-local isolation)
assert_ne!(
t1_buf, t2_buf,
"Different thread-local entropy should produce different output"
);
}
/// Test that the original RNG is properly restored after DeterministicTestRandGuard is dropped
#[test]
fn test_deterministic_rng_restoration() {
use openssl::rand::rand_bytes;
// Generate random bytes with system RNG before installing deterministic RNG
let mut before_buf = [0u8; 32];
rand_bytes(&mut before_buf).unwrap();
let deterministic_buf = {
let entropy = [0x42u8; 4096];
let nonce = [0x24u8; 48];
// Install deterministic RNG
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Generate deterministic bytes
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Expected deterministic output
let expected: [u8; 32] = [
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
];
assert_eq!(buf, expected, "Should produce deterministic output");
buf
// _rng is dropped here, should restore original RNG
};
// Generate random bytes again with restored system RNG
let mut after_buf = [0u8; 32];
rand_bytes(&mut after_buf).unwrap();
// The system RNG should produce different random values each time
// (extremely unlikely to match the deterministic output)
assert_ne!(
after_buf, deterministic_buf,
"After restoration, system RNG should produce different random values"
);
// Also verify that before and after are different (system RNG produces random values)
// Note: This could theoretically fail with probability 1/2^256, but that's negligible
assert_ne!(
before_buf, after_buf,
"System RNG should produce different random values on each call"
);
}
#[test]
fn sign_ec() {
let (ec_key, _) = get_test_keys();
@@ -603,6 +1020,112 @@ mod tests {
assert_eq!(&calc_key, &exp_key);
}
#[test]
fn derive_aes256_gcm_key_hybrid() {
let (cust_key, host_key_1, host_key_2) = get_test_keys_hybrid();
let entropy = [0x5au8; 4096];
let nonce = [0xa5u8; 48];
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
let exp_key: Aes256GcmKey = [
197, 167, 157, 112, 186, 112, 72, 125, 192, 219, 168, 132, 178, 167, 249, 123, 149, 3,
151, 166, 162, 66, 120, 39, 41, 230, 143, 54, 172, 10, 200, 143,
]
.into();
let exp_kc = [
54, 117, 96, 77, 148, 147, 170, 100, 34, 177, 95, 7, 35, 243, 145, 115, 7, 87, 178, 9,
169, 99, 193, 99, 244, 195, 23, 78, 11, 153, 221, 196, 5, 192, 253, 192, 86, 49, 194,
236, 43, 69, 183, 125, 166, 87, 158, 188, 13, 152, 19, 6, 253, 29, 194, 0, 101, 236,
28, 171, 3, 236, 53, 186, 191, 109, 7, 83, 220, 93, 126, 29, 19, 203, 201, 39, 59, 7,
131, 51, 81, 73, 254, 69, 105, 185, 214, 179, 155, 194, 189, 122, 106, 130, 249, 48, 4,
33, 245, 170, 163, 4, 223, 208, 138, 224, 203, 119, 105, 59, 187, 153, 235, 90, 79,
127, 29, 136, 230, 142, 78, 83, 27, 131, 58, 126, 76, 53, 129, 20, 85, 108, 86, 64,
244, 90, 84, 177, 239, 105, 90, 41, 118, 189, 88, 174, 224, 216, 29, 10, 123, 81, 212,
203, 197, 120, 20, 190, 3, 45, 37, 194, 208, 249, 232, 221, 67, 10, 62, 121, 143, 169,
227, 165, 17, 30, 85, 223, 44, 141, 114, 142, 105, 119, 187, 41, 46, 8, 6, 17, 29, 165,
117, 254, 92, 174, 231, 25, 117, 69, 112, 216, 80, 73, 185, 54, 50, 119, 145, 220, 174,
26, 105, 81, 114, 210, 144, 148, 109, 218, 64, 78, 231, 196, 229, 88, 46, 128, 106,
125, 204, 58, 184, 127, 193, 207, 86, 163, 98, 164, 57, 242, 29, 59, 251, 227, 185, 60,
18, 68, 74, 47, 203, 61, 164, 78, 245, 100, 87, 148, 210, 97, 158, 252, 79, 78, 50,
143, 35, 231, 215, 211, 75, 133, 214, 227, 140, 27, 21, 46, 221, 84, 89, 165, 161, 227,
46, 117, 193, 254, 190, 237, 130, 28, 57, 52, 14, 235, 154, 115, 172, 185, 67, 116, 34,
242, 158, 209, 0, 126, 196, 93, 224, 29, 246, 145, 65, 73, 185, 196, 4, 107, 124, 241,
157, 230, 168, 244, 238, 84, 188, 173, 17, 238, 26, 161, 24, 176, 229, 226, 33, 244,
167, 41, 107, 156, 29, 226, 248, 64, 146, 191, 210, 234, 76, 144, 219, 92, 136, 173,
241, 98, 0, 71, 135, 214, 196, 116, 63, 243, 73, 71, 130, 171, 86, 204, 149, 69, 164,
20, 177, 122, 95, 226, 95, 126, 106, 160, 59, 97, 137, 8, 73, 113, 189, 172, 24, 114,
60, 62, 249, 193, 3, 99, 34, 153, 42, 238, 77, 181, 80, 185, 223, 39, 8, 44, 215, 119,
214, 30, 136, 19, 215, 35, 184, 69, 94, 10, 170, 179, 51, 183, 105, 237, 237, 48, 199,
122, 159, 87, 183, 71, 230, 87, 102, 77, 81, 116, 28, 126, 195, 72, 50, 157, 223, 243,
83, 36, 16, 168, 111, 209, 132, 12, 96, 56, 140, 57, 144, 75, 253, 119, 123, 168, 2,
79, 214, 121, 80, 154, 93, 235, 222, 130, 181, 166, 97, 51, 106, 21, 138, 224, 8, 144,
223, 162, 152, 183, 6, 80, 64, 144, 21, 155, 56, 255, 108, 248, 125, 196, 46, 99, 119,
94, 104, 63, 46, 15, 165, 30, 98, 75, 212, 193, 116, 151, 189, 65, 42, 83, 253, 183,
41, 195, 45, 206, 178, 66, 36, 215, 197, 105, 236, 79, 91, 135, 164, 71, 187, 199, 200,
150, 226, 182, 254, 6, 234, 109, 3, 17, 116, 249, 44, 211, 184, 61, 189, 44, 181, 249,
8, 58, 230, 236, 8, 188, 14, 178, 100, 120, 250, 29, 1, 204, 158, 46, 161, 39, 66, 76,
42, 114, 149, 160, 31, 87, 254, 181, 224, 17, 162, 163, 99, 11, 34, 149, 50, 203, 205,
224, 38, 18, 233, 161, 49, 7, 151, 63, 81, 68, 71, 174, 49, 22, 143, 93, 50, 0, 154,
152, 178, 134, 147, 152, 118, 196, 241, 233, 67, 102, 149, 179, 213, 176, 118, 64, 172,
143, 134, 196, 232, 154, 110, 129, 155, 159, 103, 117, 202, 11, 35, 75, 104, 5, 11,
160, 147, 174, 49, 248, 45, 247, 16, 7, 64, 209, 255, 170, 243, 242, 40, 158, 94, 239,
194, 225, 113, 24, 90, 243, 73, 137, 217, 175, 130, 50, 133, 139, 250, 145, 190, 76,
151, 183, 30, 86, 146, 59, 171, 214, 211, 135, 203, 192, 42, 189, 90, 47, 152, 132,
168, 252, 175, 71, 234, 118, 207, 161, 176, 254, 189, 54, 174, 160, 178, 158, 133, 122,
63, 75, 95, 201, 55, 139, 2, 208, 232, 110, 74, 201, 196, 135, 244, 156, 87, 208, 101,
203, 121, 187, 16, 106, 80, 120, 165, 44, 147, 182, 114, 173, 186, 185, 255, 99, 85,
88, 26, 27, 43, 203, 176, 207, 88, 20, 253, 169, 210, 168, 109, 75, 234, 239, 8, 243,
244, 65, 164, 193, 255, 240, 215, 54, 158, 188, 93, 93, 54, 46, 77, 152, 78, 174, 154,
67, 248, 24, 235, 172, 240, 83, 224, 17, 100, 217, 15, 172, 176, 46, 85, 107, 105, 127,
147, 158, 202, 255, 145, 237, 84, 223, 100, 214, 38, 133, 169, 112, 227, 138, 220, 125,
72, 197, 5, 227, 94, 245, 42, 70, 33, 209, 243, 70, 229, 37, 118, 214, 147, 43, 87, 39,
241, 107, 26, 169, 28, 72, 223, 133, 145, 44, 248, 213, 52, 127, 250, 99, 193, 115,
113, 147, 89, 112, 237, 199, 208, 36, 155, 106, 144, 73, 249, 8, 116, 198, 107, 120,
233, 145, 11, 155, 178, 7, 66, 157, 255, 206, 128, 155, 233, 111, 148, 194, 214, 238,
252, 230, 96, 119, 30, 37, 73, 133, 129, 87, 185, 149, 251, 156, 17, 8, 83, 106, 207,
98, 203, 100, 39, 199, 127, 253, 59, 37, 121, 161, 216, 146, 6, 178, 183, 243, 191, 91,
106, 243, 132, 111, 216, 163, 87, 210, 197, 173, 146, 65, 131, 194, 96, 70, 6, 7, 192,
45, 173, 71, 44, 134, 122, 60, 173, 208, 238, 22, 187, 208, 212, 51, 191, 185, 174, 3,
125, 28, 134, 216, 209, 4, 224, 199, 16, 15, 56, 70, 188, 216, 92, 24, 96, 57, 125,
138, 151, 73, 254, 245, 106, 53, 4, 150, 74, 43, 42, 4, 157, 238, 125, 168, 41, 224,
22, 249, 45, 117, 32, 180, 161, 41, 39, 180, 96, 24, 2, 102, 57, 116, 34, 75, 90, 72,
134, 176, 2, 196, 59, 143, 182, 201, 117, 178, 153, 81, 108, 167, 122, 139, 71, 197,
55, 114, 60, 161, 130, 14, 29, 79, 152, 55, 136, 62, 190, 228, 202, 53, 126, 4, 173,
99, 28, 190, 224, 255, 134, 123, 166, 162, 244, 55, 26, 81, 120, 207, 193, 10, 103,
153, 215, 220, 12, 71, 67, 217, 154, 212, 44, 200, 232, 0, 178, 39, 44, 22, 7, 14, 215,
183, 192, 104, 51, 46, 93, 102, 195, 65, 9, 191, 241, 237, 151, 5, 64, 103, 228, 162,
41, 123, 29, 5, 80, 203, 198, 234, 230, 107, 53, 60, 58, 253, 47, 152, 22, 77, 81, 86,
215, 132, 152, 135, 6, 218, 46, 92, 192, 218, 198, 234, 76, 178, 25, 203, 48, 61, 76,
215, 96, 6, 49, 195, 37, 225, 10, 175, 222, 186, 133, 63, 50, 236, 215, 247, 17, 199,
8, 134, 64, 246, 194, 167, 105, 15, 57, 62, 50, 51, 243, 192, 242, 122, 11, 46, 202,
47, 10, 71, 153, 212, 226, 38, 12, 90, 150, 154, 152, 233, 7, 172, 111, 185, 160, 246,
0, 166, 113, 90, 37, 203, 166, 43, 53, 255, 211, 127, 139, 73, 7, 10, 164, 1, 168, 223,
87, 127, 43, 47, 87, 68, 84, 247, 223, 108, 108, 113, 36, 17, 50, 98, 236, 48, 10, 219,
182, 107, 240, 198, 207, 20, 178, 9, 142, 14, 93, 163, 166, 147, 38, 176, 172, 156, 73,
174, 238, 175, 231, 130, 159, 51, 128, 76, 34, 37, 138, 19, 3, 59, 71, 78, 144, 238,
226, 214, 188, 27, 42, 142, 245, 238, 131, 190, 211, 240, 41, 122, 69, 124, 171, 75,
115, 45, 144, 133, 176, 19, 81, 125, 230, 149, 235, 159, 6, 155, 195, 119, 62, 140, 50,
52, 209, 124, 3, 93, 232, 20, 130, 138, 110, 60, 183, 177, 161, 52, 114, 91, 19, 211,
156, 185, 202, 200, 36, 103, 253, 113, 45, 245, 177, 238, 43, 144, 38, 221, 0, 102, 50,
255, 20, 154, 56, 156, 155, 92, 157, 57, 209, 77, 84, 88, 24, 116, 116, 54, 213, 222,
76, 212, 193, 168, 216, 247, 125, 135, 114, 226, 128, 140, 250, 103, 82, 215, 238, 32,
74, 252, 45, 224, 23, 95, 126, 124, 135, 124, 128, 53, 203, 40, 65, 222, 8, 83, 178,
211, 64, 141, 64, 98, 188, 134, 100, 65, 166, 52, 249, 1, 206, 58, 55, 195, 23, 218,
239, 41, 73, 88, 113, 148, 132, 209, 93, 37, 205, 58, 92, 14, 1, 133, 168, 162, 192,
147, 70, 167, 101, 170, 152, 159, 0, 212, 26, 97, 49, 43, 217, 173, 38, 215, 136, 26,
208, 244, 19, 83, 207, 38, 224, 254, 92, 169, 219, 236, 172, 49, 55, 98, 55, 15, 187,
173, 114, 99, 130, 211, 78, 168, 221, 209, 250, 88, 189, 17, 186, 172, 129, 56, 90,
238, 120, 23, 176, 87, 133, 81, 244, 29, 2, 215, 34, 88, 247, 231, 167, 56,
];
let (exc_key, kc) =
super::derive_aes256_gcm_key_hybrid(&cust_key, &host_key_1, &host_key_2).unwrap();
assert_eq!(exc_key, exp_key);
assert_eq!(kc, exp_kc);
}
#[test]
fn hkdf_rfc_5869() {
use openssl::md::Md;
@@ -776,4 +1299,94 @@ mod tests {
}))
));
}
#[test]
fn validate_ec_key_valid() {
let (cust_key, host_key) = get_test_keys();
// Both test keys are SECP521R1 EC keys
assert!(validate_ec_key(&cust_key, "customer key", Nid::SECP521R1).is_ok());
assert!(validate_ec_key(&host_key, "host key", Nid::SECP521R1).is_ok());
}
#[test]
fn validate_ec_key_wrong_curve() {
let (cust_key, _) = get_test_keys();
// Test key is SECP521R1, but we expect SECP384R1
let result = validate_ec_key(&cust_key, "customer key", Nid::SECP384R1);
assert!(result.is_err());
if let Err(Error::RetrInvKey {
what,
kind,
value,
exp,
}) = result
{
assert_eq!(what, "curve");
assert_eq!(kind, "customer key");
assert_eq!(value, "secp521r1");
assert_eq!(exp, "secp384r1");
} else {
panic!("Expected RetrInvKey error");
}
}
#[test]
fn validate_ec_key_not_ec() {
let keypair = crate::get_test_asset!("keys/rsa2048key.pem");
let keypair = PKey::private_key_from_pem(keypair).unwrap();
// RSA key is not an EC key
let result = validate_ec_key(&keypair, "EC key", Nid::SECP521R1);
assert!(result.is_err());
if let Err(Error::RetrInvKey {
what,
kind,
value,
exp,
}) = result
{
assert_eq!(what, "key type");
assert_eq!(kind, "EC key");
assert_eq!(value, "RSA");
assert_eq!(exp, "EC (secp521r1)");
} else {
panic!("Expected RetrInvKey error");
}
}
#[test]
fn validate_mlkem_key_valid() {
let (_, _, mlkem_key) = get_test_keys_hybrid();
// The third key from get_test_keys_hybrid is ML-KEM-1024
assert!(validate_key_type(&mlkem_key, "ML-KEM key", KeyType::ML_KEM_1024).is_ok());
}
#[test]
fn validate_mlkem_key_wrong_type() {
let (ec_key, _) = get_test_keys();
// EC key is not ML-KEM
let result = validate_key_type(&ec_key, "EC key", KeyType::ML_KEM_1024);
assert!(result.is_err());
if let Err(Error::RetrInvKey {
what,
kind,
value,
exp,
}) = result
{
assert_eq!(what, "key type");
assert_eq!(kind, "EC key");
assert_eq!(value, "EC");
assert_eq!(exp, "ML-KEM-1024");
} else {
panic!("Expected RetrInvKey error");
}
}
}

View File

@@ -64,8 +64,11 @@ pub enum Error {
#[error("No user-key for verification provided and user-data is signed")]
BinAsrcbNoUserDataSgnKey,
#[error("Input does not contain an add-secret request version 1")]
BinAsrcbInvVersion,
#[error("Input contains an unknown add-secret request version {0}")]
BinAsrcbInvVersion(u32),
#[error("Unsupported add-secret request version: {0}")]
UnsupportedAddSecretVersion(u32),
#[error("Provided user-data key type ({key}) does not match with the user-data ({kind})")]
AsrcbUserDataKeyMismatch { key: String, kind: UserDataType },
@@ -84,6 +87,9 @@ pub enum Error {
#[error("The provided host key document in {0} contains no certificate!")]
NoHkdInFile(String),
#[error("The provided host key document in {0} contains the wrong number of keys!")]
WrongNumberOfKeys(String),
#[error("Invalid input size ({0}) for boot hdr")]
InvBootHdrSize(usize),
@@ -94,7 +100,7 @@ pub enum Error {
BinArcbInvVersion(u32),
#[error(
"The attestation request encrypted sice is to0 small {0}. Request probably tampered with."
"The attestation request encrypted sice is too small {0}. Request probably tampered with."
)]
BinArcbSeaSmall(u32),
@@ -135,6 +141,12 @@ pub enum Error {
#[error("Unsupported cipher: {:?}", .0.as_raw())]
UnsupportedCipher(Nid),
#[error("{}", .0)]
InvalidHkd(String),
#[error("All host keys must use the same version (all hybrid or all non-hybrid)")]
MixedHostkeyVersions,
}
// used in macros
@@ -173,8 +185,16 @@ pub enum HkdVerifyErrorType {
IssuerMismatch,
#[error("No CRL distribution points found")]
NoCrlDP,
#[error("CRL distribution point uses unsupported protocol (only HTTP/HTTPS allowed)")]
InvalidCrlProtocol,
#[error("The IBM Z signing key could not be verified. Error occurred at level {1}")]
IbmSignInvalid(#[source] openssl::x509::X509VerifyResult, u32),
#[error("Too many redirections during CRL download")]
TooManyRedirectionsCrlDownload,
#[error("CRL download exceeds maximum file size of {} MiB", .0 / (1024 * 1024))]
CrlDownloadTooLarge(u64),
#[error("CRL download failed")]
CrlDownloadFailed,
}
macro_rules! bail_hkd_verify {

View File

@@ -89,13 +89,16 @@ pub use crate::error::HkdVerifyErrorType;
/// Functionalities to build UV requests
pub mod request {
pub use crate::brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData};
pub use crate::brcb::{seek_se_hdr_start, BootHdrTags, SeHdrVersion, SeImgMetaData};
pub use crate::crypto::{
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey, SymKeyType,
SHA_512_HASH_LEN,
};
pub use crate::req::{EcPubKeyCoord, Encrypt, HostKey, Keyslot, ReqEncrCtx, Request};
pub use crate::req::{
EcPubKeyCoord, Encrypt, HostKey, HybridPKey, Keyslot, KeyslotV1, KeyslotV2, ReqEncrCtx,
Request,
};
pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd};
/// Reexports some useful OpenSSL symbols

View File

@@ -2,7 +2,7 @@
//
// Copyright IBM Corp.
#![expect(unused)]
#![cfg_attr(not(test), expect(unused))]
use std::ffi::CStr;
use std::ptr::NonNull;

View File

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

View File

@@ -47,7 +47,10 @@ impl EcPubKeyCoord {
/// 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> {
pub(crate) 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()?;

View File

@@ -3,6 +3,8 @@
// Copyright IBM Corp.
//! Request header structure for UV requests.
use pv_core::request::{RequestMagic, RequestVersion};
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, U32};
use crate::assert_size;
@@ -25,13 +27,13 @@ pub struct RequestHdr {
assert_size!(RequestHdr, 48);
impl RequestHdr {
pub(crate) fn new(
rqvn: u32,
pub(super) fn new(
rqvn: RequestVersion,
rql: u32,
iv: [u8; 12],
nks: u8,
sea: u32,
magic: Option<[u8; 8]>,
magic: Option<RequestMagic>,
) -> Self {
Self {
magic: magic.unwrap_or_default(),

View File

@@ -4,7 +4,56 @@
//! Host key types for UV requests
use openssl::pkey::{PKey, Public};
use openssl::nid::Nid;
use openssl::pkey::{KeyType, PKey, PKeyRef, Public};
use crate::crypto::{validate_ec_key, validate_key_type};
pub use crate::error::Result;
/// Hybrid public key (ECDH and ML-KEM)
#[derive(Clone, Debug)]
pub struct HybridPKey {
/// ECDH public key
pub(super) ec_key: PKey<Public>,
/// ML-KEM public key
pub(super) mlkem_key: PKey<Public>,
}
impl HybridPKey {
/// Creates a new hybrid public key with validation.
///
/// # Parameters
/// - `ec_key`: ECDH public key (must be SECP521R1)
/// - `mlkem_key`: ML-KEM public key (must be ML-KEM-1024)
///
/// # Errors
/// Returns an error if:
/// - EC key is not SECP521R1 curve
/// - ML-KEM key is not ML-KEM-1024
pub fn new(ec_key: PKey<Public>, mlkem_key: PKey<Public>) -> Result<Self> {
validate_ec_key(&ec_key, "ECDH key", Nid::SECP521R1)?;
validate_key_type(&mlkem_key, "ML-KEM key", KeyType::ML_KEM_1024)?;
Ok(Self { ec_key, mlkem_key })
}
/// Returns a reference to the EC key
pub fn ec_key(&self) -> &PKeyRef<Public> {
&self.ec_key
}
/// Returns a reference to the ML-KEM key
pub fn mlkem_key(&self) -> &PKeyRef<Public> {
&self.mlkem_key
}
}
impl AsRef<HybridPKey> for HybridPKey {
fn as_ref(&self) -> &HybridPKey {
self
}
}
/// Versioned host keys container
#[non_exhaustive]
@@ -12,15 +61,33 @@ use openssl::pkey::{PKey, Public};
pub enum HostKey {
/// ECDH public key
V1(PKey<Public>),
/// Hybrid public key (ECDH and ML-KEM)
V2(HybridPKey),
}
impl HostKey {
/// Return the ECDH public key
pub fn ec_key(&self) -> &PKey<Public> {
match self {
pub fn ec_key(&self) -> Option<&PKeyRef<Public>> {
Some(match self {
HostKey::V1(ec_key) => ec_key,
HostKey::V2(hybrid) => hybrid.ec_key(),
})
}
/// Return the ML-KEM public key
pub fn mlkem_key(&self) -> Option<&PKeyRef<Public>> {
match self {
HostKey::V1(_) => None,
HostKey::V2(hybrid) => Some(hybrid.mlkem_key()),
}
}
/// Test if the hostkey is hybrid
#[must_use]
pub fn is_hybrid(&self) -> bool {
matches!(self, HostKey::V2(_))
}
}
impl AsRef<HostKey> for HostKey {
@@ -28,3 +95,248 @@ impl AsRef<HostKey> for HostKey {
self
}
}
#[cfg(test)]
mod tests {
use openssl::ec::{EcGroup, EcKey};
use openssl::pkey::Private;
use super::*;
use crate::openssl_extensions::generate_ml_kem;
use crate::test_utils::get_test_key_and_cert_hybrid;
use crate::Error;
fn to_public_key(key: &PKey<Private>) -> Result<PKey<Public>> {
let der = key.public_key_to_der()?;
Ok(PKey::public_key_from_der(&der)?)
}
#[test]
fn test_hostkey_v1_variant() {
let (_, ec_key) = crate::test_utils::get_test_key_and_cert();
let hostkey = HostKey::V1(ec_key.public_key().unwrap());
assert!(!hostkey.is_hybrid(), "V1 HostKey should not be hybrid");
assert!(matches!(hostkey, HostKey::V1(_)));
}
#[test]
fn test_hostkey_v2_variant() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let hostkey = HostKey::V2(hybrid);
assert!(hostkey.is_hybrid(), "V2 HostKey should be hybrid");
assert!(matches!(hostkey, HostKey::V2(_)));
}
#[test]
fn test_hostkey_ec_key_access() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let v1_key = HostKey::V1(ec_key.public_key().unwrap());
let v2_key = HostKey::V2(hybrid);
assert!(v1_key.ec_key().unwrap().public_key_to_der().is_ok());
assert!(v2_key.ec_key().unwrap().public_key_to_der().is_ok());
}
#[test]
fn test_hostkey_v2_mlkem_key_access() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let hostkey = HostKey::V2(hybrid);
assert!(hostkey.mlkem_key().unwrap().public_key_to_der().is_ok());
}
#[test]
fn test_hostkey_v1_has_no_mlkem_key() {
let (_, ec_key) = crate::test_utils::get_test_key_and_cert();
let hostkey = HostKey::V1(ec_key.public_key().unwrap());
assert!(hostkey.mlkem_key().is_none());
}
#[test]
fn test_hybrid_public_key_structure() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
// Verify both components are present and valid
assert!(!hybrid.ec_key().public_key_to_der().unwrap().is_empty());
assert!(!hybrid.mlkem_key().public_key_to_der().unwrap().is_empty());
}
#[test]
fn test_hybrid_pkey_invalid_ec_curve() {
// Generate a P-256 key instead of P-521
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
let wrong_ec_key = PKey::from_ec_key(EcKey::generate(&group).unwrap()).unwrap();
let (_, _, mlkem_key) = get_test_key_and_cert_hybrid();
let result = HybridPKey::new(
to_public_key(&wrong_ec_key).unwrap(),
mlkem_key.public_key().unwrap(),
);
assert!(result.is_err(), "Should reject EC key with wrong curve");
if let Err(Error::RetrInvKey {
what,
kind,
value,
exp,
}) = result
{
assert_eq!(what, "curve");
assert_eq!(kind, "ECDH key");
assert_eq!(value, "prime256v1");
assert_eq!(exp, "secp521r1");
} else {
panic!("Expected RetrInvKey error for wrong curve");
}
}
#[test]
fn test_hybrid_pkey_invalid_mlkem_type() {
let (_, ec_key, _) = get_test_key_and_cert_hybrid();
// Generate ML-KEM-512 instead of ML-KEM-1024
let wrong_mlkem_key = generate_ml_kem(KeyType::ML_KEM_512).unwrap();
let result = HybridPKey::new(
ec_key.public_key().unwrap(),
to_public_key(&wrong_mlkem_key).unwrap(),
);
assert!(result.is_err(), "Should reject ML-KEM key with wrong type");
if let Err(Error::RetrInvKey {
what,
kind,
value,
exp,
}) = result
{
assert_eq!(what, "key type");
assert_eq!(kind, "ML-KEM key");
assert_eq!(value, "ML-KEM-512");
assert_eq!(exp, "ML-KEM-1024");
} else {
panic!("Expected RetrInvKey error for wrong ML-KEM type");
}
}
#[test]
fn test_hybrid_pkey_clone() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let cloned = hybrid.clone();
// Verify both original and clone have valid keys
assert!(!hybrid.ec_key().public_key_to_der().unwrap().is_empty());
assert!(!hybrid.mlkem_key().public_key_to_der().unwrap().is_empty());
assert!(!cloned.ec_key().public_key_to_der().unwrap().is_empty());
assert!(!cloned.mlkem_key().public_key_to_der().unwrap().is_empty());
// Verify the keys are equivalent
assert_eq!(
hybrid.ec_key().public_key_to_der().unwrap(),
cloned.ec_key().public_key_to_der().unwrap()
);
assert_eq!(
hybrid.mlkem_key().public_key_to_der().unwrap(),
cloned.mlkem_key().public_key_to_der().unwrap()
);
}
#[test]
fn test_hostkey_clone() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
// Test cloning V1
let v1_key = HostKey::V1(ec_key.public_key().unwrap());
let v1_cloned = v1_key.clone();
assert!(!v1_cloned.is_hybrid());
assert_eq!(
v1_key.ec_key().unwrap().public_key_to_der().unwrap(),
v1_cloned.ec_key().unwrap().public_key_to_der().unwrap()
);
// Test cloning V2
let v2_key = HostKey::V2(hybrid);
let v2_cloned = v2_key.clone();
assert!(v2_cloned.is_hybrid());
assert_eq!(
v2_key.ec_key().unwrap().public_key_to_der().unwrap(),
v2_cloned.ec_key().unwrap().public_key_to_der().unwrap()
);
assert_eq!(
v2_key.mlkem_key().unwrap().public_key_to_der().unwrap(),
v2_cloned.mlkem_key().unwrap().public_key_to_der().unwrap()
);
}
#[test]
fn test_hybrid_pkey_as_ref() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let hybrid_ref: &HybridPKey = hybrid.as_ref();
assert!(!hybrid_ref.ec_key().public_key_to_der().unwrap().is_empty());
assert!(!hybrid_ref
.mlkem_key()
.public_key_to_der()
.unwrap()
.is_empty());
}
#[test]
fn test_hostkey_as_ref() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let v1_key = HostKey::V1(ec_key.public_key().unwrap());
let v1_ref: &HostKey = v1_key.as_ref();
assert!(!v1_ref.is_hybrid());
let v2_key = HostKey::V2(hybrid);
let v2_ref: &HostKey = v2_key.as_ref();
assert!(v2_ref.is_hybrid());
}
}

View File

@@ -4,13 +4,11 @@
//! IBM Z Host key-slot implementations.
use openssl::hash::MessageDigest;
use openssl::hash::{DigestBytes, 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::crypto::{derive_aes256_gcm_key, derive_aes256_gcm_key_hybrid, encrypt_aead, hash};
use crate::req::{EcPubKeyCoord, Encrypt, HostKey, HybridPKey};
use crate::Result;
/// IBM Z Host key-slot
@@ -71,12 +69,107 @@ impl Encrypt for KeyslotV1 {
}
}
/// IBM Z hybrid (V2) Host key-slot
///
/// Layout in binary format:
/// ```none
/// _______________________________________________________________
/// | Public Host Key Hash (64) |
/// | Wrapped(=Encrypted) Request Protection Key(32) |
/// | Key Slot Tag (16) |
/// | ML-KEM Ciphertext (1568) |
/// |_____________________________________________________________|
/// ```
#[derive(Debug, Clone)]
pub struct KeyslotV2 {
ec_hostkey: PKey<Public>,
mlkem_hostkey: PKey<Public>,
}
impl KeyslotV2 {
/// Size of a hybrid host-key hash
pub const PHKH_SIZE: u32 = 0x40;
/// Size of a complete V2 keyslot in bytes
pub const SIZE: usize = 1680;
/// Creates a new HybridKeyslot from the provided hybrid public key
pub fn new(hostkey: HybridPKey) -> Self {
let HybridPKey { ec_key, mlkem_key } = hostkey;
Self {
ec_hostkey: ec_key,
mlkem_hostkey: mlkem_key,
}
}
/// calculates the sha512 of this hybrid key
pub fn sha512(&self) -> Result<DigestBytes> {
let mut phk_buf = Vec::<u8>::with_capacity(160 + 1568);
let ec_phk: EcPubKeyCoord = self.ec_hostkey.as_ref().try_into()?;
phk_buf.extend_from_slice(ec_phk.as_ref());
phk_buf.extend_from_slice(&self.mlkem_hostkey.raw_public_key()?);
assert_eq!(phk_buf.len(), 160 + 1568);
let hash = hash(MessageDigest::sha512(), &phk_buf)?;
assert_eq!(hash.len(), 64);
Ok(hash)
}
/// 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.
pub fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef<Private>) -> Result<Vec<u8>> {
let mut res = Vec::with_capacity(1680);
self.encrypt_to(secret, priv_key, &mut res)?;
Ok(res)
}
/// 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 HybridKeyslot.
///
/// # 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, ciphertext) =
derive_aes256_gcm_key_hybrid(priv_key, &self.ec_hostkey, &self.mlkem_hostkey)?;
let mut wrpk_and_kst =
encrypt_aead(&derived_key.into(), &[0; 12], &[], prot_key)?.into_buf();
assert_eq!(wrpk_and_kst.len(), 48);
to.reserve(1680);
let hash = self.sha512()?;
assert_eq!(hash.len(), 64);
to.extend_from_slice(&hash);
to.append(&mut wrpk_and_kst);
assert_eq!(ciphertext.len(), 1568);
to.extend_from_slice(&ciphertext);
Ok(())
}
}
/// Versioned keyslot container
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Keyslot {
/// V1 key-slots with ECDH keys
V1(KeyslotV1),
/// V2 key-slots with hybrid ECDH/ML-KEM keys
V2(KeyslotV2),
}
impl Keyslot {
@@ -84,27 +177,34 @@ impl Keyslot {
pub fn new(hostkey: HostKey) -> Self {
match hostkey {
HostKey::V1(key) => Keyslot::V1(KeyslotV1::new(key)),
HostKey::V2(key) => Keyslot::V2(KeyslotV2::new(key)),
}
}
/// Return the public host key hash size for the given version of the key-slot in bytes
#[must_use]
pub fn phkh_size(&self) -> u32 {
match self {
Keyslot::V1(_) => KeyslotV1::PHKH_SIZE,
Keyslot::V2(_) => KeyslotV2::PHKH_SIZE,
}
}
/// Return the size of the key-slot in bytes
#[must_use]
pub fn size(&self) -> usize {
match self {
Keyslot::V1(_) => KeyslotV1::SIZE,
Keyslot::V2(_) => KeyslotV2::SIZE,
}
}
/// Return whether the key-slot uses hybrid keys
#[must_use]
pub fn is_hybrid(&self) -> bool {
match self {
Keyslot::V1(_) => false,
Keyslot::V2(_) => true,
}
}
}
@@ -118,6 +218,7 @@ impl Encrypt for Keyslot {
) -> Result<()> {
match self {
Keyslot::V1(ks) => ks.encrypt_to(secret, priv_key, to),
Keyslot::V2(ks) => ks.encrypt_to(secret, priv_key, to),
}
}
}
@@ -128,11 +229,17 @@ impl From<PKey<Public>> for Keyslot {
}
}
impl From<HybridPKey> for Keyslot {
fn from(key: HybridPKey) -> Self {
Keyslot::V2(KeyslotV2::new(key))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::get_test_asset;
use crate::test_utils::*;
use crate::test_utils::{DeterministicTestRandGuard, *};
#[test]
fn keyslot() {
@@ -147,4 +254,214 @@ mod tests {
let encr_ks = keyslot.encrypt(&[0x16u8; 32], &cust_key).unwrap();
assert_ne!(exp_keyslot, encr_ks);
}
#[test]
fn keyslot_v2() {
// Install deterministic RNG for reproducible encryption
let _guard = DeterministicTestRandGuard::install(&[0x42; 64], &[0x17; 16]).unwrap();
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
let exp_keyslot = vec![
255, 94, 191, 53, 220, 196, 47, 37, 93, 227, 234, 101, 1, 174, 171, 68, 42, 136, 92,
238, 72, 6, 17, 77, 231, 225, 174, 22, 222, 188, 212, 15, 248, 145, 72, 126, 139, 17,
233, 225, 156, 46, 233, 151, 54, 2, 175, 88, 215, 254, 243, 222, 37, 81, 50, 110, 18,
76, 252, 12, 210, 146, 66, 23, 123, 246, 141, 14, 70, 157, 73, 124, 205, 112, 192, 82,
160, 243, 2, 154, 134, 145, 176, 147, 30, 217, 221, 93, 170, 239, 165, 37, 30, 192, 47,
94, 129, 165, 12, 137, 239, 12, 180, 199, 240, 160, 180, 205, 117, 84, 16, 148, 51, 48,
243, 216, 50, 132, 187, 115, 137, 3, 109, 145, 172, 159, 224, 25, 63, 96, 241, 105, 89,
35, 42, 180, 248, 35, 166, 81, 116, 175, 179, 252, 81, 188, 189, 42, 17, 60, 231, 132,
32, 111, 90, 138, 201, 16, 3, 97, 246, 130, 173, 85, 218, 187, 232, 92, 52, 120, 56,
142, 137, 101, 31, 21, 194, 186, 198, 38, 240, 58, 150, 93, 86, 18, 186, 5, 12, 41, 37,
94, 162, 119, 16, 237, 82, 252, 178, 159, 57, 58, 162, 20, 173, 140, 10, 172, 203, 241,
94, 110, 117, 32, 36, 217, 7, 168, 50, 105, 43, 33, 174, 140, 55, 88, 26, 233, 81, 253,
98, 121, 190, 70, 146, 84, 186, 6, 51, 181, 2, 59, 216, 45, 78, 4, 82, 90, 21, 180, 99,
239, 195, 254, 33, 171, 124, 97, 36, 110, 119, 142, 98, 125, 70, 236, 51, 192, 32, 8,
214, 224, 101, 169, 45, 173, 137, 194, 78, 114, 224, 76, 174, 188, 133, 227, 167, 169,
47, 241, 190, 15, 145, 217, 87, 254, 15, 150, 52, 138, 192, 230, 52, 129, 36, 8, 40,
37, 126, 103, 85, 86, 25, 78, 53, 243, 107, 193, 18, 102, 3, 83, 212, 5, 252, 224, 241,
7, 205, 220, 168, 183, 15, 37, 52, 190, 12, 9, 152, 124, 210, 65, 152, 210, 199, 81,
95, 240, 141, 196, 236, 8, 249, 221, 116, 130, 42, 52, 208, 218, 180, 27, 249, 59, 245,
21, 165, 142, 234, 140, 75, 89, 136, 250, 155, 157, 12, 154, 152, 188, 178, 227, 237,
124, 111, 176, 131, 227, 238, 164, 22, 88, 80, 217, 42, 217, 80, 148, 118, 12, 136, 95,
53, 69, 209, 131, 74, 91, 193, 253, 229, 146, 11, 58, 125, 19, 8, 229, 60, 177, 244,
74, 41, 92, 91, 62, 64, 221, 20, 213, 227, 4, 40, 22, 177, 155, 236, 147, 165, 27, 4,
99, 4, 151, 155, 82, 72, 151, 22, 102, 183, 192, 242, 18, 104, 146, 205, 107, 21, 254,
88, 23, 69, 246, 57, 217, 249, 124, 246, 54, 163, 244, 38, 74, 215, 144, 50, 45, 142,
36, 216, 88, 39, 70, 67, 88, 130, 1, 8, 205, 240, 159, 210, 205, 233, 237, 60, 81, 176,
112, 172, 187, 121, 239, 198, 43, 17, 49, 55, 170, 228, 243, 255, 76, 72, 121, 125,
157, 250, 93, 251, 55, 25, 4, 129, 67, 195, 30, 37, 6, 76, 10, 240, 178, 255, 151, 138,
36, 255, 32, 237, 133, 162, 130, 91, 9, 238, 67, 134, 4, 86, 225, 179, 166, 219, 4,
144, 7, 125, 248, 75, 132, 54, 56, 51, 237, 206, 252, 96, 9, 208, 140, 127, 143, 180,
32, 179, 254, 18, 233, 187, 122, 159, 172, 77, 204, 9, 179, 242, 153, 18, 138, 82, 13,
210, 140, 207, 9, 217, 216, 241, 106, 205, 109, 195, 223, 82, 172, 181, 48, 91, 124,
235, 85, 17, 18, 166, 216, 86, 120, 185, 54, 147, 156, 84, 106, 30, 235, 234, 114, 27,
23, 121, 210, 251, 136, 117, 53, 177, 28, 153, 144, 17, 70, 190, 206, 27, 135, 42, 227,
39, 243, 86, 170, 11, 63, 191, 102, 89, 92, 137, 103, 59, 142, 236, 203, 156, 231, 255,
30, 5, 86, 53, 59, 76, 181, 218, 184, 244, 21, 50, 100, 72, 81, 98, 193, 34, 58, 67,
150, 15, 17, 162, 216, 119, 213, 203, 50, 27, 158, 61, 105, 134, 151, 200, 68, 103, 74,
207, 12, 5, 163, 30, 198, 28, 41, 41, 97, 93, 251, 213, 133, 41, 225, 178, 67, 160, 85,
132, 146, 1, 201, 99, 49, 185, 27, 150, 213, 165, 134, 45, 248, 204, 67, 145, 49, 81,
35, 246, 25, 215, 209, 159, 106, 212, 14, 149, 193, 163, 90, 24, 83, 230, 178, 216,
194, 130, 118, 169, 81, 49, 49, 145, 96, 206, 216, 15, 134, 43, 130, 25, 110, 84, 39,
175, 223, 183, 209, 123, 5, 166, 244, 19, 89, 2, 47, 226, 3, 93, 156, 163, 67, 156,
237, 17, 59, 69, 99, 94, 32, 180, 64, 64, 115, 75, 44, 241, 150, 43, 169, 17, 36, 125,
216, 132, 92, 3, 244, 16, 83, 179, 192, 65, 133, 208, 19, 156, 17, 60, 54, 29, 253,
237, 18, 158, 145, 142, 232, 147, 2, 91, 21, 145, 125, 204, 243, 161, 245, 110, 140,
219, 206, 70, 235, 211, 167, 138, 104, 132, 248, 157, 65, 153, 216, 47, 125, 205, 237,
137, 220, 25, 228, 146, 194, 10, 169, 201, 224, 88, 119, 38, 140, 120, 125, 140, 46,
184, 221, 30, 10, 47, 34, 140, 173, 64, 38, 48, 77, 236, 206, 163, 111, 80, 46, 40,
232, 63, 247, 222, 25, 20, 246, 143, 9, 107, 172, 180, 84, 188, 234, 102, 87, 181, 173,
83, 14, 163, 170, 91, 29, 209, 93, 52, 158, 213, 6, 91, 71, 54, 244, 189, 198, 60, 21,
131, 210, 35, 18, 36, 164, 188, 87, 54, 73, 208, 115, 11, 248, 57, 107, 93, 23, 49,
129, 221, 61, 12, 172, 31, 199, 129, 196, 5, 184, 78, 226, 210, 83, 232, 153, 64, 17,
119, 243, 45, 73, 50, 129, 35, 94, 243, 146, 86, 136, 202, 86, 87, 97, 193, 59, 160,
181, 95, 150, 148, 117, 96, 31, 151, 97, 159, 53, 72, 171, 239, 210, 67, 207, 201, 109,
160, 230, 235, 176, 35, 235, 98, 128, 166, 195, 144, 200, 156, 7, 72, 242, 15, 95, 99,
69, 41, 219, 254, 244, 80, 158, 177, 64, 83, 11, 235, 98, 201, 130, 176, 119, 22, 214,
135, 215, 37, 65, 108, 91, 34, 94, 0, 153, 161, 87, 144, 177, 12, 122, 205, 11, 72,
233, 213, 63, 37, 12, 255, 235, 188, 194, 189, 65, 169, 185, 207, 131, 182, 243, 233,
30, 245, 39, 42, 188, 157, 28, 121, 231, 194, 121, 250, 121, 11, 28, 252, 98, 151, 238,
124, 56, 138, 106, 116, 187, 203, 21, 75, 239, 132, 19, 62, 43, 42, 12, 214, 87, 225,
217, 169, 134, 183, 193, 241, 175, 140, 36, 147, 248, 86, 6, 58, 212, 72, 112, 0, 56,
168, 128, 68, 253, 173, 225, 152, 167, 138, 254, 97, 123, 227, 163, 135, 198, 49, 49,
138, 249, 234, 245, 78, 150, 140, 170, 199, 41, 206, 246, 19, 117, 241, 27, 112, 102,
4, 94, 237, 30, 0, 68, 252, 163, 205, 10, 63, 146, 147, 37, 153, 197, 52, 162, 50, 250,
219, 130, 197, 3, 60, 246, 133, 83, 140, 103, 227, 50, 212, 121, 165, 114, 139, 225,
195, 145, 107, 249, 127, 194, 23, 112, 141, 242, 14, 218, 42, 131, 19, 245, 143, 73,
79, 194, 135, 224, 171, 249, 169, 129, 160, 153, 75, 66, 26, 12, 254, 180, 212, 229,
172, 134, 159, 122, 212, 219, 21, 29, 33, 11, 176, 149, 73, 169, 26, 150, 96, 133, 90,
217, 18, 37, 244, 48, 249, 4, 180, 129, 9, 45, 219, 106, 215, 28, 81, 118, 48, 98, 109,
167, 72, 107, 187, 78, 127, 251, 184, 170, 74, 57, 188, 91, 196, 229, 251, 70, 163, 68,
227, 238, 71, 115, 155, 246, 146, 97, 208, 21, 245, 62, 127, 47, 79, 131, 217, 41, 153,
52, 237, 159, 60, 98, 18, 169, 149, 6, 84, 135, 2, 45, 170, 165, 213, 201, 127, 23,
209, 2, 158, 235, 240, 195, 255, 76, 54, 189, 113, 50, 105, 230, 191, 217, 29, 46, 181,
80, 197, 60, 177, 243, 61, 52, 24, 140, 134, 147, 176, 198, 22, 92, 156, 217, 189, 134,
17, 122, 53, 49, 14, 87, 128, 99, 207, 123, 113, 169, 195, 206, 127, 211, 21, 216, 166,
18, 137, 110, 148, 70, 26, 54, 52, 113, 189, 45, 89, 254, 218, 247, 193, 71, 19, 153,
35, 179, 49, 237, 199, 176, 251, 143, 95, 115, 195, 3, 122, 161, 243, 220, 39, 102,
147, 134, 25, 172, 164, 167, 110, 123, 221, 177, 28, 236, 100, 165, 186, 179, 45, 189,
183, 76, 171, 127, 209, 108, 220, 83, 207, 136, 98, 54, 76, 37, 188, 57, 157, 204, 109,
150, 181, 110, 57, 5, 26, 168, 34, 11, 117, 3, 184, 147, 155, 122, 244, 251, 215, 1,
211, 226, 185, 214, 120, 206, 212, 75, 203, 174, 140, 20, 93, 93, 207, 28, 15, 122, 9,
83, 98, 107, 51, 202, 151, 220, 42, 95, 17, 141, 141, 201, 149, 253, 55, 169, 170, 237,
166, 92, 92, 20, 89, 124, 167, 102, 161, 87, 97, 88, 20, 245, 175, 32, 111, 0, 2, 192,
25, 63, 182, 10, 226, 165, 162, 223, 35, 243, 198, 189, 167, 137, 134, 207, 84, 240, 8,
95, 137, 31, 159, 58, 211, 161, 94, 150, 54, 188, 145, 253, 206, 156, 130, 121, 192,
221, 73, 249, 92, 91, 184, 211, 131, 206, 205, 183, 159, 195, 170, 47, 13, 131, 6, 132,
103, 121, 40, 252, 250, 251, 85, 253, 68, 66, 211, 24, 104, 48, 150, 176, 62, 201, 161,
93, 204, 120, 196, 159, 208, 199, 96, 228, 239, 29, 239, 128, 156, 14, 131, 25, 27,
157, 249, 253, 250, 193, 148, 133, 59, 138, 202, 239,
];
let keyslot = Keyslot::new(host_key);
let encr_keyslot = keyslot.encrypt(&[0x17u8; 32], &cust_key).unwrap();
assert_eq!(encr_keyslot, exp_keyslot);
}
#[test]
fn test_keyslot_v2_constants() {
// Test V2 keyslot constants
assert_eq!(
KeyslotV2::SIZE,
1680,
"V2 keyslot size should be 1680 bytes"
);
assert_eq!(
KeyslotV2::PHKH_SIZE,
0x40,
"V2 PHKH size should be 64 bytes (SHA-512)"
);
}
#[test]
fn test_keyslot_v2_creation() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let keyslot = KeyslotV2::new(hybrid);
// Verify keyslot was created successfully
assert!(keyslot.ec_hostkey.public_key_to_der().is_ok());
assert!(keyslot.mlkem_hostkey.public_key_to_der().is_ok());
}
#[test]
fn test_keyslot_enum_v1_variant() {
let (_, host_key) = get_test_keys();
let keyslot = Keyslot::V1(KeyslotV1(host_key));
assert_eq!(keyslot.phkh_size(), KeyslotV1::PHKH_SIZE);
assert_eq!(keyslot.size(), KeyslotV1::SIZE);
assert!(matches!(keyslot, Keyslot::V1(_)));
}
#[test]
fn test_keyslot_enum_v2_variant() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let keyslot = Keyslot::V2(KeyslotV2::new(hybrid));
assert_eq!(keyslot.phkh_size(), KeyslotV2::PHKH_SIZE);
assert_eq!(keyslot.size(), KeyslotV2::SIZE);
assert!(matches!(keyslot, Keyslot::V2(_)));
}
#[test]
fn test_keyslot_from_hostkey_v1() {
let (_, ec_key) = get_test_keys();
let hostkey = HostKey::V1(ec_key);
let keyslot = Keyslot::new(hostkey);
assert!(matches!(keyslot, Keyslot::V1(_)));
assert_eq!(keyslot.size(), KeyslotV1::SIZE);
}
#[test]
fn test_keyslot_from_hostkey_v2() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let hostkey = HostKey::V2(hybrid);
let keyslot = Keyslot::new(hostkey);
assert!(matches!(keyslot, Keyslot::V2(_)));
assert_eq!(keyslot.size(), KeyslotV2::SIZE);
}
#[test]
fn test_keyslot_from_hybrid_public_key() {
let (_, ec_key, mlkem_key) = get_test_key_and_cert_hybrid();
let hybrid = HybridPKey::new(
ec_key.public_key().unwrap(),
mlkem_key.public_key().unwrap(),
)
.unwrap();
let keyslot: Keyslot = hybrid.into();
assert!(matches!(keyslot, Keyslot::V2(_)));
}
#[test]
fn test_keyslot_sizes() {
// Document the sizes for V1 and V2
assert_eq!(KeyslotV1::SIZE, 80, "V1 keyslot size");
assert_eq!(
KeyslotV2::SIZE,
1680,
"V2 keyslot size (includes ML-KEM ciphertext)"
);
}
}

View File

@@ -17,9 +17,10 @@ mod request;
// Re-export public types
pub use context::ReqEncrCtx;
pub(crate) use ec_coord::get_pub_ecdh_points;
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 hostkey::{HostKey, HybridPKey};
pub use keyslot::{Keyslot, KeyslotV1, KeyslotV2};
pub use request::{BinReqValues, Request};

View File

@@ -51,7 +51,7 @@ pub trait Request {
/// 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);
fn add_hostkey(&mut self, hostkey: HostKey) -> Result<()>;
}
/// A struct to represent some parts of a binary/encrypted request.
@@ -79,7 +79,7 @@ impl<'a> BinReqValues<'a> {
let rql = hdr.rql.get() as usize;
let sea = hdr.sea.get() as usize;
if rql < req.len() || sea + Self::TAG_LEN > rql {
if req.len() < rql || sea + Self::TAG_LEN > rql {
return Err(Error::BinRequestSmall);
}
let aad_size = rql - sea - Self::TAG_LEN;
@@ -148,10 +148,9 @@ mod tests {
use zerocopy::IntoBytes;
use super::*;
use crate::crypto::SymKey;
use crate::get_test_asset;
use crate::req::header::RequestHdr;
use crate::req::{Aad, HostKey, Keyslot, ReqEncrCtx};
use crate::request::SymKey;
use crate::req::{Aad, HybridPKey, Keyslot};
use crate::test_utils::*;
static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes();
@@ -187,6 +186,37 @@ mod tests {
assert_eq!(&aad, &aad_exp);
}
#[test]
fn encr_build_aad_v2() {
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
let ks = Keyslot::new(HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()));
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 aad_exp = vec![
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr
0, 0, 2, 0, // vers
0, 0, 6, 232, // 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
];
// only compare non-randomized part
assert_eq!(aad[..aad_exp.len()], aad_exp);
}
#[test]
fn encr_build_aad_nks_no() {
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
@@ -210,6 +240,21 @@ mod tests {
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::ManyHostkeys)));
}
#[test]
fn encr_build_aad_nks_many_v2() {
let (_, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
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();
@@ -229,6 +274,25 @@ mod tests {
assert_eq!(aad.get(39).unwrap(), &3u8);
}
#[test]
fn encr_build_aad_nks_v2() {
let (_, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
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);
@@ -262,4 +326,18 @@ mod tests {
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn bin_req_values_buffer_too_small() {
// Create a valid header with request length set to 200 bytes
let hdr = RequestHdr::new(0x200, 200, [0x11; 12], 1, 32, Some(TEST_MAGIC));
let hdr_bin = hdr.as_bytes();
// Create a buffer that's smaller than the declared request length (48
// bytes)
let small_buffer = Vec::from(hdr_bin);
let result = BinReqValues::get(&small_buffer);
assert!(matches!(result, Err(Error::BinRequestSmall)));
}
}

View File

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

View File

@@ -4,6 +4,7 @@
use std::mem::size_of;
use pv_core::static_assert;
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
@@ -14,7 +15,7 @@ use crate::misc::Flags;
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
use crate::uv::UvFlags;
use crate::{assert_size, static_assert, Error, Result};
use crate::{assert_size, Error, Result};
#[cfg(doc)]
use crate::{
request::SymKeyType,
@@ -60,9 +61,9 @@ use crate::{
/// let hkd = s390_pv::misc::read_certs(&std::fs::read("host-key-document.crt")?)?;
/// // IBM issued HKD certificates typically have one X509
/// let hkd = hkd.first().unwrap().public_key()?;
/// arcb.add_hostkey(HostKey::V1(hkd));
/// arcb.add_hostkey(HostKey::V1(hkd))?;
/// // you can add multiple hostkeys
/// // arcb.add_hostkey(another_hkd);
/// // arcb.add_hostkey(HostKey::V1(another_hkd))?;
/// // encrypt it
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
/// let arcb = arcb.encrypt(&ctx)?;
@@ -106,7 +107,13 @@ impl AttestationRequest {
/// Returns a reference to the flags of this [`AttestationRequest`].
pub fn flags(&self) -> &AttestationFlags {
&self.aad.flags
self.aad.flags()
}
/// Returns the request version, derived from the type of added host-keys.
/// Returns [`AttestationVersion::One`] if no host-keys have been added yet.
pub fn version(&self) -> AttestationVersion {
self.version
}
/// Returns a copy of the confidential data of this [`AttestationRequest`].
@@ -142,6 +149,7 @@ impl AttestationRequest {
let values = BinReqValues::get(arcb)?;
match values.version().try_into()? {
AttestationVersion::One => (),
AttestationVersion::Two => (),
};
Ok(values)
@@ -235,8 +243,19 @@ impl Request for AttestationRequest {
ctx.encrypt_aead(&aad, conf).map(|res| res.into_buf())
}
fn add_hostkey(&mut self, hostkey: HostKey) {
self.keyslots.push(Keyslot::new(hostkey))
fn add_hostkey(&mut self, hostkey: HostKey) -> Result<()> {
match self.version {
AttestationVersion::One if !hostkey.is_hybrid() => Ok(()),
AttestationVersion::Two if hostkey.is_hybrid() => Ok(()),
AttestationVersion::One => Err(Error::InvalidHkd(
"Add classical hostkey to a v1 attestation request".to_string(),
)),
AttestationVersion::Two => Err(Error::InvalidHkd(
"Add hybrid key to a v2 attetstation request".to_string(),
)),
}?;
self.keyslots.push(Keyslot::new(hostkey));
Ok(())
}
}
@@ -246,6 +265,8 @@ impl Request for AttestationRequest {
pub enum AttestationVersion {
/// Version 1 (= 0x0100)
One = 0x0100,
/// Version 2 (= 0x0200)
Two = 0x0200,
}
impl TryFrom<u32> for AttestationVersion {
@@ -254,6 +275,8 @@ impl TryFrom<u32> for AttestationVersion {
fn try_from(value: u32) -> Result<Self> {
if value == Self::One as u32 {
Ok(Self::One)
} else if value == Self::Two as u32 {
Ok(Self::Two)
} else {
Err(Error::BinArcbInvVersion(value))
}
@@ -412,8 +435,8 @@ impl Zeroize for ReqConfData {
mod test {
use super::*;
use crate::get_test_asset;
use crate::request::SymKey;
use crate::test_utils::get_test_keys;
use crate::request::{HybridPKey, SymKey};
use crate::test_utils::{get_test_keys, get_test_keys_hybrid};
const ARPK: [u8; 32] = [0x17; 32];
const NONCE: [u8; 16] = [0xab; 16];
@@ -421,6 +444,8 @@ mod test {
fn mk_arcb() -> Vec<u8> {
let (cust_key, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]),
Some(cust_key),
@@ -443,7 +468,37 @@ mod test {
arcb.conf.value_mut().nonce = NONCE;
arcb.conf.value_mut().meas_key = MEAS;
arcb.add_hostkey(HostKey::V1(host_key));
arcb.add_hostkey(host_key).unwrap();
arcb.encrypt(&ctx).unwrap()
}
fn mk_arcb_v2() -> Vec<u8> {
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]),
Some(cust_key),
Some(SymKey::Aes256(ARPK.into())),
)
.unwrap();
let mut flags = AttestationFlags::default();
flags.set_image_phkh();
flags.set_attest_phkh();
let mut arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
flags,
)
.unwrap();
// manually set confidential data (API does not allow this)
arcb.conf.value_mut().nonce = NONCE;
arcb.conf.value_mut().meas_key = MEAS;
arcb.add_hostkey(host_key).unwrap();
arcb.encrypt(&ctx).unwrap()
}
@@ -455,6 +510,32 @@ mod test {
assert_eq!(request, exp);
}
#[test]
fn arcb_v2() {
let request = mk_arcb_v2();
// Expected bytes for a V2 ARCB: rqvn = 0x0200 (bytes 8-11), rest deterministic.
// The first 288 bytes cover header + customer-public-key + one V2 keyslot header.
let exp: [u8; 288] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 7, 208, 85, 85, 85, 85, 85, 85, 85, 85, 85,
85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 80, 112, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199,
93, 52, 249, 22, 82, 219, 69, 123, 11, 32, 156, 70, 164, 145, 164, 78, 226, 177, 110,
35, 194, 216, 218, 241, 22, 103, 138, 98, 242, 76, 227, 50, 197, 153, 95, 8, 69, 107,
102, 177, 109, 213, 90, 146, 197, 7, 241, 227, 26, 247, 140, 100, 168, 46, 122, 84, 27,
21, 19, 80, 21, 242, 2, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 128, 88,
167, 241, 165, 195, 80, 151, 83, 58, 2, 169, 56, 121, 231, 222, 103, 186, 40, 11, 206,
131, 101, 236, 148, 178, 185, 8, 245, 137, 195, 169, 152, 216, 190, 30, 99, 7, 215, 74,
224, 26, 220, 70, 130, 95, 246, 187, 111, 160, 92, 17, 71, 207, 226, 204, 244, 162, 79,
61, 131, 61, 218, 112, 255, 94, 191, 53, 220, 196, 47, 37, 93, 227, 234, 101, 1, 174,
171, 68, 42, 136, 92, 238, 72, 6, 17, 77, 231, 225, 174, 22, 222, 188, 212, 15, 248,
145, 72, 126, 139, 17, 233, 225, 156, 46, 233, 151, 54, 2, 175, 88, 215, 254, 243, 222,
37, 81, 50, 110, 18, 76, 252, 12, 210, 146, 66, 23,
];
// only compare non-randomized part
assert_eq!(request[..288], exp[..288]);
}
#[test]
fn auth_bin() {
let request = mk_arcb();
@@ -464,6 +545,15 @@ mod test {
assert_eq!(exp, auth_bin.as_bytes());
}
#[test]
fn auth_bin_v2() {
let request = mk_arcb_v2();
let auth_bin = AttestationRequest::auth_bin(&request).unwrap();
let exp = &request[0x30..0x40];
assert_eq!(exp, auth_bin.as_bytes());
}
#[test]
fn decrypt_bin() {
let request = mk_arcb();
@@ -473,6 +563,100 @@ mod test {
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
}
#[test]
fn decrypt_bin_v2() {
let request = mk_arcb_v2();
let arpk = SymKey::Aes256(ARPK.into());
let (_, conf) = AttestationRequest::decrypt_bin(&request, &arpk).unwrap();
assert_eq!(conf.measurement_key(), &MEAS);
assert_eq!(conf.nonce().as_ref().unwrap().value(), &NONCE);
}
#[test]
fn arcb_v1_version() {
// Without any host-keys, version defaults to One
let arcb = AttestationRequest::new(
AttestationVersion::One,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
assert_eq!(arcb.version(), AttestationVersion::One);
}
#[test]
fn arcb_v2_version() {
// After adding a V2 host-key, version is Two
let mut arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
let (_, host_key1, host_key2) = get_test_keys_hybrid();
arcb.add_hostkey(HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()))
.unwrap();
assert_eq!(arcb.version(), AttestationVersion::Two);
}
#[test]
fn attestation_version_try_from() {
// Test version conversion from u32
assert_eq!(
AttestationVersion::try_from(0x0100).unwrap(),
AttestationVersion::One
);
assert_eq!(
AttestationVersion::try_from(0x0200).unwrap(),
AttestationVersion::Two
);
// Invalid version should error
assert!(AttestationVersion::try_from(0x0300).is_err());
}
#[test]
fn attestation_flags_expected_size() {
// Test expected additional data size calculation for V1
let mut flags = AttestationFlags::default();
// Image PHKH flag - should be 32 bytes
flags.set_image_phkh();
assert_eq!(flags.expected_additional_size(), 32);
// Add attest PHKH flag - should be 64 bytes (32 + 32)
flags.set_attest_phkh();
assert_eq!(flags.expected_additional_size(), 64);
// Add secret store hash - should be 128 bytes (64 + 64)
flags.set_secret_store_hash();
assert_eq!(flags.expected_additional_size(), 128);
// Add firmware state - should be 448 bytes (128 + 320)
flags.set_firmware_state();
assert_eq!(flags.expected_additional_size(), 448);
}
#[test]
fn confidential_data_v2() {
// Test confidential data extraction (version-independent)
let arcb = AttestationRequest::new(
AttestationVersion::Two,
AttestationMeasAlg::HmacSha512,
AttestationFlags::default(),
)
.unwrap();
let conf = arcb.confidential_data();
// Should have measurement key and nonce
assert_eq!(conf.measurement_key().len(), 64);
assert!(conf.nonce().is_some());
assert_eq!(conf.nonce().as_ref().unwrap().value().len(), 16);
}
#[test]
fn decrypt_bin_fail_magic() {
let arpk = SymKey::Aes256(ARPK.into());
@@ -484,6 +668,17 @@ mod test {
assert!(matches!(ret, Err(Error::NoArcb)));
}
#[test]
fn decrypt_bin_fail_magic_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper magic
tamp_arcb[0] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::NoArcb)));
}
#[test]
fn decrypt_bin_fail_mai() {
let arpk = SymKey::Aes256(ARPK.into());
@@ -499,6 +694,21 @@ mod test {
));
}
#[test]
fn decrypt_bin_fail_mai_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper MAI
tamp_arcb[0x3b] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
println!("{ret:?}");
assert!(matches!(
ret,
Err(Error::PvCore(pv_core::Error::BinArcbInvAlgorithm(17)))
));
}
#[test]
fn decrypt_bin_fail_aad() {
let arpk = SymKey::Aes256(ARPK.into());
@@ -509,4 +719,15 @@ mod test {
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
}
#[test]
fn decrypt_bin_fail_aad_v2() {
let arpk = SymKey::Aes256(ARPK.into());
let mut tamp_arcb = mk_arcb_v2();
// tamper AAD
tamp_arcb[0x3c] = 17;
let ret = AttestationRequest::decrypt_bin(&tamp_arcb, &arpk);
assert!(matches!(ret, Err(Error::GcmTagMismatch)));
}
}

View File

@@ -6,6 +6,7 @@ use std::mem::size_of;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKeyRef, Private};
use pv_core::misc::write_file;
use zerocopy::{BigEndian, IntoBytes, U16, U32};
use super::AttNonce;
@@ -101,6 +102,7 @@ impl AttestationItems {
}
items.extend_from_slice(additional);
assert!(items.len() == size);
write_file("additional_data_parsed", &items, "blah").unwrap();
Self(items.into())
}
}

View File

@@ -6,31 +6,32 @@ use openssl::md::Md;
use openssl::pkey::{PKey, Private};
use pv_core::request::RequestVersion;
use pv_core::secret::AddSecretMagic;
use pv_core::static_assert;
use pv_core::uv::SecretId;
use zerocopy::{Immutable, IntoBytes};
use super::guest_secret::ListableSecretHdr;
use super::guest_secret::{ListableSecretHdr, SecretAuth};
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::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx, RequestHdr};
use crate::request::{BootHdrTags, Confidential, Request};
use crate::secret::{ExtSecret, GuestSecret};
use crate::uv::{ConfigUid, UvFlags};
use crate::{assert_size, Result};
use crate::{assert_size, Error, Result};
/// Authenticated data w/o user data
#[repr(C)]
#[derive(Debug, Clone, Copy, IntoBytes, Immutable)]
struct ReqAuthData {
struct ReqAuthDataV1 {
flags: UvFlags,
boot_tags: BootHdrTags,
cuid: ConfigUid,
reserved90: [u8; 0x100],
}
assert_size!(ReqAuthData, 0x1e8);
assert_size!(ReqAuthDataV1, 0x1e8);
impl ReqAuthData {
impl ReqAuthDataV1 {
fn new<F: Into<UvFlags>>(boot_tags: BootHdrTags, flags: F) -> Self {
Self {
flags: flags.into(),
@@ -41,6 +42,30 @@ impl ReqAuthData {
}
}
/// Authenticated data w/o user data for v2 header: move up secret header 2
#[repr(C)]
#[derive(Debug, Clone, IntoBytes, Immutable)]
struct ReqAuthDataV2 {
flags: UvFlags,
boot_tags: BootHdrTags,
cuid: ConfigUid,
secr_auth: [u8; 0x30],
reservedd0: [u8; 0x100 - 0x30],
}
assert_size!(ReqAuthDataV2, 0x1e8);
impl ReqAuthDataV2 {
fn new<F: Into<UvFlags>>(boot_tags: BootHdrTags, flags: F, secr_auth: [u8; 0x30]) -> Self {
Self {
flags: flags.into(),
boot_tags,
cuid: [0; 0x10],
secr_auth,
reservedd0: [0; 0xd0],
}
}
}
#[derive(Debug)]
struct ReqConfData {
secret: GuestSecret,
@@ -92,6 +117,8 @@ impl From<AddSecretFlags> for UvFlags {
pub enum AddSecretVersion {
/// Version 1 (= 0x0100)
One = 0x0100,
/// Version 2 (= 0x0200)
Two = 0x0200,
#[cfg(not(doc))]
#[cfg(any(debug_assertions, test))]
@@ -99,17 +126,182 @@ pub enum AddSecretVersion {
Inv = 0,
}
impl TryFrom<u32> for AddSecretVersion {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
if value == Self::One as u32 {
Ok(Self::One)
} else if value == Self::Two as u32 {
Ok(Self::Two)
} else {
Err(Error::BinAsrcbInvVersion(value))
}
}
}
impl From<AddSecretVersion> for RequestVersion {
fn from(val: AddSecretVersion) -> Self {
val as Self
}
}
impl From<crate::request::SeHdrVersion> for AddSecretVersion {
fn from(val: crate::request::SeHdrVersion) -> Self {
match val {
crate::request::SeHdrVersion::One => AddSecretVersion::One,
crate::request::SeHdrVersion::Two => AddSecretVersion::Two,
}
}
}
/// Trait for authenticated data in add-secret requests.
///
/// This trait provides a common interface for different versions of authenticated data,
/// allowing flexible addition of new versions in the future.
trait ReqAuthData: IntoBytes + Immutable {
/// Get the configuration UID
fn cuid(&self) -> &ConfigUid;
/// Set the configuration UID
fn set_cuid(&mut self, cuid: ConfigUid);
/// Get the boot tags
fn boot_tags(&self) -> &BootHdrTags;
/// Get the flags
fn flags(&self) -> &UvFlags;
}
impl ReqAuthData for ReqAuthDataV1 {
fn cuid(&self) -> &ConfigUid {
&self.cuid
}
fn set_cuid(&mut self, cuid: ConfigUid) {
self.cuid = cuid;
}
fn boot_tags(&self) -> &BootHdrTags {
&self.boot_tags
}
fn flags(&self) -> &UvFlags {
&self.flags
}
}
impl ReqAuthData for ReqAuthDataV2 {
fn cuid(&self) -> &ConfigUid {
&self.cuid
}
fn set_cuid(&mut self, cuid: ConfigUid) {
self.cuid = cuid;
}
fn boot_tags(&self) -> &BootHdrTags {
&self.boot_tags
}
fn flags(&self) -> &UvFlags {
&self.flags
}
}
/// Enum holding version-specific authenticated data
#[derive(Debug)]
enum ReqAuthDataVersion {
V1(ReqAuthDataV1),
V2(ReqAuthDataV2),
}
impl ReqAuthDataVersion {
fn new(
version: AddSecretVersion,
boot_tags: BootHdrTags,
flags: AddSecretFlags,
conf_data: &SecretAuth,
) -> Result<Self> {
Ok(match version {
AddSecretVersion::One => Self::V1(ReqAuthDataV1::new(boot_tags, flags)),
AddSecretVersion::Two => Self::V2(ReqAuthDataV2::new(
boot_tags,
flags,
conf_data.get(version).try_into().expect(
"SecretAuth::get() must return exactly 0x30 bytes for AddSecretVersion::Two",
),
)),
#[cfg(any(debug_assertions, test))]
AddSecretVersion::Inv => panic!("Invalid version for production use"),
})
}
#[allow(dead_code)]
fn cuid(&self) -> &ConfigUid {
match self {
Self::V1(v) => v.cuid(),
Self::V2(v) => v.cuid(),
}
}
fn set_cuid(&mut self, cuid: ConfigUid) {
match self {
Self::V1(v) => v.set_cuid(cuid),
Self::V2(v) => v.set_cuid(cuid),
}
}
fn boot_tags(&self) -> &BootHdrTags {
match self {
Self::V1(v) => v.boot_tags(),
Self::V2(v) => v.boot_tags(),
}
}
#[allow(dead_code)]
fn flags(&self) -> &UvFlags {
match self {
Self::V1(v) => v.flags(),
Self::V2(v) => v.flags(),
}
}
fn as_bytes(&self) -> &[u8] {
match self {
Self::V1(v) => v.as_bytes(),
Self::V2(v) => v.as_bytes(),
}
}
}
/// Add-secret request Control Block
///
/// An ASRCB wraps a secret to securely transport it to the Ultravisor.
///
/// Layout:
/// Layout V2:
/// ```none
/// _______________________________________________________________
/// | generic header (48)
/// | --------------------------------------------------- |
/// | Plaintext Add-Secret flags (8) |
/// | SE header tags: PLD(64) ALD(64) TLD(64) HeaderTag(16) |
/// | Configuration unique ID(16) (Attestation) |
/// | Optional, defaults to 0 |
/// | Secret header (48) |
/// | Reserved(208) |
/// | User Data(512) (reserved) |
/// | Customer Public Key (160) generated for each request |
/// | N Keyslots(1680 each) |
/// | --------------------------------------------------- |
/// | Secret to add (Secret type dependent)(may be 0 bytes) | Encrypted
/// | Extension secret(32) Optional, defaults to 0 | Encrypted
/// | --------------------------------------------------- |
/// | AES GCM Tag (16) |
/// |_____________________________________________________________|
/// ```
///
/// Layout V1:
/// ```none
/// _______________________________________________________________
/// | generic header (48)
@@ -133,15 +325,19 @@ impl From<AddSecretVersion> for RequestVersion {
#[derive(Debug)]
pub struct AddSecretRequest {
version: AddSecretVersion,
aad: ReqAuthData,
aad: ReqAuthDataVersion,
keyslots: Vec<Keyslot>,
conf: ReqConfData,
user_data: UserData,
}
static_assert!(AddSecretRequest::USER_DATA_OFFS == 0x218);
static_assert!(
AddSecretRequest::USER_DATA_OFFS == size_of::<RequestHdr>() + size_of::<ReqAuthDataV2>()
);
impl AddSecretRequest {
/// Offset of the user-data in the add-secret request in bytes
pub(super) const V1_USER_DATA_OFFS: usize = 0x218;
pub(super) const USER_DATA_OFFS: usize = size_of::<RequestHdr>() + size_of::<ReqAuthDataV1>();
/// Create a new add-secret request.
///
@@ -152,22 +348,23 @@ impl AddSecretRequest {
secret: GuestSecret,
boot_tags: BootHdrTags,
flags: AddSecretFlags,
) -> Self {
Self {
conf: ReqConfData {
extension_secret: Confidential::new([0; 32]),
secret,
},
aad: ReqAuthData::new(boot_tags, flags),
) -> Result<Self> {
let conf = ReqConfData {
extension_secret: Confidential::new([0; 32]),
secret,
};
Ok(Self {
aad: ReqAuthDataVersion::new(version, boot_tags, flags, &conf.secret.auth())?,
conf,
keyslots: vec![],
version,
user_data: UserData::Null,
}
})
}
/// Sets the Configuration Unique Id of this [`AddSecretRequest`].
pub fn set_cuid(&mut self, cuid: ConfigUid) {
self.aad.cuid = cuid;
self.aad.set_cuid(cuid);
}
/// Sets the extension secret of this [`AddSecretRequest`].
@@ -182,7 +379,7 @@ impl AddSecretRequest {
ExtSecret::Derived(cck) => hkdf_rfc_5869(
Md::sha512(),
cck.value(),
self.aad.boot_tags.tag(),
self.aad.boot_tags().tag(),
DER_EXT_SECRET_INFO,
)?
.into(),
@@ -221,7 +418,6 @@ impl AddSecretRequest {
let cust_pub_key = ctx.key_coords()?;
let secr_auth = self.conf.secret.auth();
let user_data = self.user_data.data();
let mut aad: Vec<Aad> = Vec::with_capacity(5 + self.keyslots.len());
aad.push(Aad::Plain(self.aad.as_bytes()));
if let Some(data) = user_data.0 {
@@ -232,7 +428,13 @@ impl AddSecretRequest {
}
aad.push(Aad::Plain(cust_pub_key.as_ref()));
self.keyslots.iter().for_each(|k| aad.push(Aad::Ks(k)));
aad.push(Aad::Plain(secr_auth.get()));
// write secret header (1) only for v1
match self.version {
AddSecretVersion::One => aad.push(Aad::Plain(secr_auth.get(AddSecretVersion::One))),
AddSecretVersion::Two => {}
#[cfg(any(debug_assertions, test))]
_ => return Err(Error::UnsupportedAddSecretVersion(self.version as u32)),
}
ctx.build_aad(self.version.into(), &aad, conf_len, self.user_data.magic())
}
@@ -273,7 +475,7 @@ impl AddSecretRequest {
// add signature to authenticated data starting with USER_DATA_OFFS
self.user_data.sign(
&mut buf[aad_range.start..encr_range.end],
Self::V1_USER_DATA_OFFS,
Self::USER_DATA_OFFS,
)?;
// encrypt again with signed data
@@ -309,7 +511,101 @@ impl Request for AddSecretRequest {
}
}
fn add_hostkey(&mut self, hostkey: HostKey) {
self.keyslots.push(Keyslot::new(hostkey))
fn add_hostkey(&mut self, hostkey: HostKey) -> Result<()> {
match self.version {
AddSecretVersion::One if !hostkey.is_hybrid() => Ok(()),
AddSecretVersion::Two if hostkey.is_hybrid() => Ok(()),
AddSecretVersion::One => Err(Error::InvalidHkd(
"Add classical hostkey to a v1 attestation request".to_string(),
)),
AddSecretVersion::Two => Err(Error::InvalidHkd(
"Add hybrid key to a v2 attetstation request".to_string(),
)),
#[cfg(any(debug_assertions, test))]
AddSecretVersion::Inv => panic!("Invalid version for production use"),
}?;
self.keyslots.push(Keyslot::new(hostkey));
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::request::SeHdrVersion;
#[test]
fn add_secret_version_v1() {
// Test V1 version constant
assert_eq!(AddSecretVersion::One as u32, 0x0100);
}
#[test]
fn add_secret_version_v2() {
// Test V2 version constant
assert_eq!(AddSecretVersion::Two as u32, 0x0200);
}
#[test]
fn add_secret_version_conversion_v1() {
// Test conversion from SeHdrVersion to AddSecretVersion for V1
let v1: AddSecretVersion = SeHdrVersion::One.into();
assert_eq!(v1, AddSecretVersion::One);
}
#[test]
fn add_secret_version_conversion_v2() {
// Test conversion from SeHdrVersion to AddSecretVersion for V2
let v2: AddSecretVersion = SeHdrVersion::Two.into();
assert_eq!(v2, AddSecretVersion::Two);
}
#[test]
fn add_secret_version_into_request_version() {
// Test conversion from AddSecretVersion to RequestVersion
let v1: RequestVersion = AddSecretVersion::One.into();
assert_eq!(v1, 0x0100);
let v2: RequestVersion = AddSecretVersion::Two.into();
assert_eq!(v2, 0x0200);
}
#[test]
fn add_secret_flags_default() {
// Test default flags have no bits set
let flags = AddSecretFlags::default();
let uv_flags: UvFlags = flags.into();
// Default should have all bits cleared
assert_eq!(uv_flags.as_bytes(), &[0u8; 8]);
}
#[test]
fn add_secret_flags_disable_dump() {
// Test disable dump flag sets bit 0
let mut flags = AddSecretFlags::default();
flags.set_disable_dump();
let uv_flags: UvFlags = flags.into();
// Bit 0 should be set, so bytes should not be all zeros
assert_ne!(uv_flags.as_bytes(), &[0u8; 8]);
}
#[test]
fn req_auth_data_size() {
// Test ReqAuthData size constant
use std::mem::size_of;
assert_eq!(size_of::<ReqAuthDataV1>(), 0x1e8);
}
#[test]
#[cfg(any(debug_assertions, test))]
fn add_secret_version_inv_for_testing() {
// Test that invalid version exists for testing
assert_eq!(AddSecretVersion::Inv as u32, 0);
assert_ne!(AddSecretVersion::Inv, AddSecretVersion::One);
assert_ne!(AddSecretVersion::Inv, AddSecretVersion::Two);
}
}

View File

@@ -16,6 +16,7 @@ use super::asrcb::AddSecretRequest;
use crate::crypto::{hash, random_array, SymKeyType};
use crate::request::openssl::{NID_ED25519, NID_ED448};
use crate::request::Confidential;
use crate::secret::AddSecretVersion;
use crate::uv::{
AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
RetrieveCmd, SecretId,
@@ -388,12 +389,18 @@ pub(crate) enum SecretAuth {
}
impl SecretAuth {
const NULL_HDR: NullSecretHdr = NullSecretHdr::new();
const NULL_HDR_V1: NullSecretHdrV1 = NullSecretHdrV1::new();
const NULL_HDR_V2: NullSecretHdrV2 = NullSecretHdrV2::new();
const UPDATE_CCK_HDR: UpdateCckHdr = UpdateCckHdr::new();
pub fn get(&self) -> &[u8] {
pub fn get(&self, version: AddSecretVersion) -> &[u8] {
match self {
Self::Null => Self::NULL_HDR.as_bytes(),
Self::Null => match version {
AddSecretVersion::One => Self::NULL_HDR_V1.as_bytes(),
AddSecretVersion::Two => Self::NULL_HDR_V2.as_bytes(),
#[cfg(any(debug_assertions, test))]
_ => panic!("Invalid AddSecretVersion"),
},
Self::Listable(h) => h.as_bytes(),
Self::UpdateCck => Self::UPDATE_CCK_HDR.as_bytes(),
}
@@ -402,15 +409,15 @@ impl SecretAuth {
#[repr(C)]
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
struct NullSecretHdr {
struct NullSecretHdrV1 {
res0: u16,
kind: U16<BigEndian>,
secret_len: U32<BigEndian>,
res8: u64,
}
assert_size!(NullSecretHdr, 0x10);
assert_size!(NullSecretHdrV1, 0x10);
impl NullSecretHdr {
impl NullSecretHdrV1 {
const fn new() -> Self {
Self {
res0: 0,
@@ -421,6 +428,29 @@ impl NullSecretHdr {
}
}
#[repr(C)]
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
struct NullSecretHdrV2 {
res0: u16,
kind: U16<BigEndian>,
secret_len: U32<BigEndian>,
res8: u64,
reserved1: [u8; 32],
}
assert_size!(NullSecretHdrV2, 0x30);
impl NullSecretHdrV2 {
const fn new() -> Self {
Self {
res0: 0,
kind: U16::new(ListableSecretType::NULL),
secret_len: U32::ZERO,
res8: 0,
reserved1: [0; 32],
}
}
}
#[repr(C)]
#[derive(Debug, IntoBytes, FromBytes, Immutable, KnownLayout)]
pub(crate) struct ListableSecretHdr {
@@ -701,7 +731,7 @@ mod test {
let gs_bytes = gs.auth();
let exp = vec![0u8, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
assert_eq!(exp, gs_bytes.get());
assert_eq!(exp, gs_bytes.get(AddSecretVersion::One));
assert_eq!(&Vec::<u8>::new(), gs.confidential())
}
@@ -716,7 +746,7 @@ mod test {
let mut exp = vec![0u8, 0, 0, 2, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
exp.extend([1; 32]);
assert_eq!(exp, gs_bytes_auth.get());
assert_eq!(exp, gs_bytes_auth.get(AddSecretVersion::One));
assert_eq!(&[2; 32], gs.confidential());
}
@@ -729,7 +759,7 @@ mod test {
secret: vec![2; 32].into(),
};
let auth = gs.auth();
let gs_bytes_auth = auth.get();
let gs_bytes_auth = auth.get(AddSecretVersion::One);
let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
exp.extend([1; 32]);
@@ -746,7 +776,7 @@ mod test {
let mut exp = vec![0u8, 0, 0, 0x16, 0, 0, 0, 0x20];
exp.extend([0; 40]);
assert_eq!(exp, gs_bytes_auth.get());
assert_eq!(exp, gs_bytes_auth.get(AddSecretVersion::One));
assert_eq!(&[2; 32], gs.confidential());
}
}

View File

@@ -133,6 +133,7 @@ impl RetrievedSecret {
RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
}
}
/// Get the data in PEM format.
///
/// # Errors

View File

@@ -10,7 +10,7 @@ use crate::crypto::{sign_msg, verify_signature};
use crate::req::BinReqValues;
use crate::request::openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public};
use crate::request::RequestMagic;
use crate::secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType};
use crate::secret::{AddSecretMagic, AddSecretRequest, UserDataType};
use crate::{assert_size, Error, Result};
/// User data.
@@ -261,21 +261,19 @@ pub fn verify_asrcb_and_get_user_data(
// check that the provided buffer contains an Add Secret request
let magic = AddSecretMagic::try_from_bytes(&asrcb)?;
let req = BinReqValues::get(&asrcb)?;
if req.version() != AddSecretVersion::One as u32 {
return Err(Error::BinAsrcbInvVersion);
}
// preventing the two lines after the truncate from panicking
let req_len = req.len();
if asrcb.len() < req_len
|| req_len < AddSecretRequest::V1_USER_DATA_OFFS + UserData::USER_DATA_SIZE
|| req_len < AddSecretRequest::USER_DATA_OFFS + UserData::USER_DATA_SIZE
{
return Err(pv_core::Error::NoAsrcb.into());
}
// forget the tag (and all additional data that might be behind the tag)
asrcb.truncate(req_len - BinReqValues::TAG_LEN);
// get a mutable refrenence on the 512 bytes of user data
let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::V1_USER_DATA_OFFS);
let (_, user_data) = asrcb.split_at_mut(AddSecretRequest::USER_DATA_OFFS);
let user_data = &mut user_data[..UserData::USER_DATA_SIZE];
// depending on the user_data_type do:

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -6,13 +6,13 @@
use openssl::ec::{EcGroup, EcKey};
use openssl::nid::Nid;
use openssl::pkey::Private;
use s390_pv::request::openssl::pkey::{PKey, Public};
use s390_pv::request::{BootHdrTags, HostKey, ReqEncrCtx, Request, SymKey};
use s390_pv::request::openssl::pkey::PKey;
use s390_pv::request::{BootHdrTags, HostKey, HybridPKey, ReqEncrCtx, Request, SymKey};
use s390_pv::secret::{
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret,
GuestSecret,
};
use s390_pv::test_utils::get_test_keys;
use s390_pv::test_utils::{get_test_keys, get_test_keys_hybrid, DeterministicTestRandGuard};
use s390_pv::uv::ConfigUid;
use s390_pv::{get_test_asset, Result};
@@ -29,8 +29,13 @@ fn create_asrcb(
hkd: HostKey,
ctx: &ReqEncrCtx,
) -> Result<Vec<u8>> {
let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags);
let asrcb = match hkd {
HostKey::V1(_) => AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags),
HostKey::V2(_) => AddSecretRequest::new(AddSecretVersion::Two, guest_secret, TAGS, flags),
_ => unreachable!("Unknown HostKey version"),
};
let mut asrcb = asrcb?;
if let Some(s) = ext_secret {
asrcb.set_ext_secret(s)?
};
@@ -38,12 +43,25 @@ fn create_asrcb(
asrcb.set_cuid(c);
};
asrcb.add_hostkey(hkd);
asrcb.add_hostkey(hkd)?;
asrcb.encrypt(ctx)
}
fn get_crypto() -> (PKey<Public>, ReqEncrCtx) {
fn get_crypto() -> (HostKey, ReqEncrCtx) {
let (cust_key, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]),
Some(cust_key),
Some(SymKey::Aes256([0x17; 32].into())),
)
.unwrap();
(host_key, ctx)
}
fn get_crypto_v2() -> (HostKey, ReqEncrCtx) {
let (cust_key, host_key1, host_key2) = get_test_keys_hybrid();
let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap());
let ctx = ReqEncrCtx::new_aes_256(
Some([0x55; 12]),
Some(cust_key),
@@ -67,14 +85,25 @@ where
true => Some(CUID),
false => None,
};
create_asrcb(
guest_secret,
ext_secret.into(),
flags,
cuid,
HostKey::V1(host_key),
&ctx,
)
create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx)
}
fn gen_asrcb_v2<E>(
guest_secret: GuestSecret,
ext_secret: E,
flags: AddSecretFlags,
cuid: bool,
) -> Result<Vec<u8>>
where
E: Into<Option<ExtSecret>>,
{
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let (host_key, ctx) = get_crypto_v2();
let cuid = match cuid {
true => Some(CUID),
false => None,
};
create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx)
}
fn association() -> GuestSecret {
@@ -96,9 +125,21 @@ fn no_flag() -> AddSecretFlags {
fn create_signed_asrcb(skey: PKey<Private>, user_data: Vec<u8>) -> Vec<u8> {
let (host_key, ctx) = get_crypto();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
asrcb.add_hostkey(HostKey::V1(host_key));
asrcb.add_hostkey(host_key).unwrap();
asrcb.set_user_data(user_data, Some(skey)).unwrap();
asrcb.encrypt(&ctx).unwrap()
}
fn create_signed_asrcb_v2(skey: PKey<Private>, user_data: Vec<u8>) -> Vec<u8> {
let (host_key, ctx) = get_crypto_v2();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::Two, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
asrcb.add_hostkey(host_key).unwrap();
asrcb.set_user_data(user_data, Some(skey)).unwrap();
asrcb.encrypt(&ctx).unwrap()
}
@@ -108,9 +149,10 @@ fn null_none_default_ncuid_one_user_unsgn() {
let user_data_orig = vec![0x56; 0x183];
let (host_key, ctx) = get_crypto();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
asrcb.add_hostkey(HostKey::V1(host_key));
asrcb.add_hostkey(host_key).unwrap();
asrcb.set_user_data(user_data_orig.clone(), None).unwrap();
let asrcb = asrcb.encrypt(&ctx).unwrap();
@@ -121,6 +163,7 @@ fn null_none_default_ncuid_one_user_unsgn() {
&user_data.as_ref().unwrap()[..user_data_orig.len()]
);
}
#[test]
fn null_none_default_ncuid_one_user_ec() {
let (usr_sgn_key, _) = get_test_keys();
@@ -242,8 +285,11 @@ fn null_none_default_ncuid_one() {
fn null_none_default_cuid_seven() {
let (hkd, ctx) = get_crypto();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
(0..7).for_each(|_| asrcb.add_hostkey(HostKey::V1(hkd.clone())));
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
for _ in 0..7 {
asrcb.add_hostkey(hkd.clone()).unwrap()
}
asrcb.set_cuid(CUID);
let asrcb = asrcb.encrypt(&ctx).unwrap();
@@ -259,3 +305,164 @@ fn verify_no_user_data() {
Ok(None)
))
}
// V2 tests (using hybrid keys)
#[test]
fn null_none_default_ncuid_two_user_unsgn() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let user_data_orig = vec![0x56; 0x183];
let (host_key, ctx) = get_crypto_v2();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::Two, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
asrcb.add_hostkey(host_key).unwrap();
asrcb.set_user_data(user_data_orig.clone(), None).unwrap();
let asrcb = asrcb.encrypt(&ctx).unwrap();
let user_data = verify_asrcb_and_get_user_data(asrcb, None).unwrap();
assert_eq!(
user_data_orig.as_slice(),
&user_data.as_ref().unwrap()[..user_data_orig.len()]
);
}
#[test]
fn null_none_default_ncuid_two_user_ec() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let (usr_sgn_key, _) = get_test_keys();
let usr_vrfy_key = usr_sgn_key.ec_key().unwrap();
let usr_vrfy_key = usr_vrfy_key.public_key();
let usr_vrfy_key = PKey::from_ec_key(
EcKey::from_public_key(
&EcGroup::from_curve_name(Nid::SECP521R1).unwrap(),
usr_vrfy_key,
)
.unwrap(),
)
.unwrap();
let user_data_orig = vec![0x56; 0x100];
let asrcb = create_signed_asrcb_v2(usr_sgn_key, user_data_orig.clone());
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
assert_eq!(
user_data_orig.as_slice(),
&user_data.as_ref().unwrap()[..user_data_orig.len()]
);
}
#[test]
fn null_none_default_ncuid_two_user_rsa2048() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let usr_sgn_key = get_test_asset!("keys/rsa2048key.pem");
let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap();
let user_data_orig = vec![0x56; 0x100];
let asrcb = create_signed_asrcb_v2(usr_sgn_key, user_data_orig.clone());
let usr_vrfy_key = get_test_asset!("keys/rsa2048key.pub.pem");
let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap();
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
assert_eq!(
user_data_orig.as_slice(),
&user_data.as_ref().unwrap()[..user_data_orig.len()]
);
}
#[test]
fn null_none_default_ncuid_two_user_rsa3072() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let usr_sgn_key = get_test_asset!("keys/rsa3072key.pem");
let usr_sgn_key = PKey::private_key_from_pem(usr_sgn_key).unwrap();
let user_data_orig = vec![0x56; 0x80];
let asrcb = create_signed_asrcb_v2(usr_sgn_key, user_data_orig.clone());
let usr_vrfy_key = get_test_asset!("keys/rsa3072key.pub.pem");
let usr_vrfy_key = PKey::public_key_from_pem(usr_vrfy_key).unwrap();
let user_data = verify_asrcb_and_get_user_data(asrcb, Some(usr_vrfy_key)).unwrap();
assert_eq!(
user_data_orig.as_slice(),
&user_data.as_ref().unwrap()[..user_data_orig.len()]
);
}
#[test]
fn null_none_default_cuid_two() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let asrcb = gen_asrcb_v2(GuestSecret::Null, None, no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn assoc_none_default_cuid_two() {
let asrcb = gen_asrcb_v2(association(), None, no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/assoc_none_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn null_simple_default_cuid_two() {
let asrcb = gen_asrcb_v2(GuestSecret::Null, ext_simple(), no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/null_simple_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn assoc_simple_default_cuid_two() {
let asrcb = gen_asrcb_v2(association(), ext_simple(), no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/assoc_simple_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn null_derived_default_cuid_two() {
let asrcb = gen_asrcb_v2(GuestSecret::Null, ext_derived(), no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/null_derived_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn assoc_derived_default_cuid_two() {
let asrcb = gen_asrcb_v2(association(), ext_derived(), no_flag(), true).unwrap();
let exp = get_test_asset!("exp/asrcb/assoc_derived_default_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn null_none_dump_cuid_two() {
let mut flags = no_flag();
flags.set_disable_dump();
let asrcb = gen_asrcb_v2(GuestSecret::Null, None, flags, true).unwrap();
let exp = get_test_asset!("exp/asrcb/null_none_dump_cuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn null_none_default_ncuid_two() {
let asrcb = gen_asrcb_v2(GuestSecret::Null, None, no_flag(), false).unwrap();
let exp = get_test_asset!("exp/asrcb/null_none_default_ncuid_two");
assert_eq!(asrcb, exp);
}
#[test]
fn null_none_default_cuid_seven_two() {
let _guard = DeterministicTestRandGuard::install(&[0x42; 4096], &[0x11; 32]).unwrap();
let (hkd, ctx) = get_crypto_v2();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::Two, GuestSecret::Null, TAGS, no_flag())
.expect("AddSecretRequest::new failed");
for _ in 0..7 {
asrcb.add_hostkey(hkd.clone()).unwrap()
}
asrcb.set_cuid(CUID);
let asrcb = asrcb.encrypt(&ctx).unwrap();
let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_seven_two");
assert_eq!(asrcb, exp);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,37 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGjDCCBHSgAwIBAgIUKp3mjstxJ9gXt3S7lpKjri2dEUwwDQYJKoZIhvcNAQEL
MIIGkzCCBHugAwIBAgIUd+Tu7LLVqsmeGQbJ5IAjvORmZzAwDQYJKoZIhvcNAQEL
BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0
MDMyMTE0NTIzOFoYDzIzODgxMjIzMTQ1MjM4WjCBvTELMAkGA1UEBhMCVVMxNDAy
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI2
MDcwMjA4NDc0N1oYDzIzOTEwNDA1MDg0NzQ3WjCBvTELMAkGA1UEBhMCVVMxNDAy
BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp
b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y
cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxHjAc
BgNVBAsMFUlCTSBaIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAK+kNJHAUL4dcMnjElivyuzkO2UwTUlJcKQCBmWRHYQjRbP5
akJx8SZ+wI0Spo00hG4sV2BxA83J+Yrked53DEzlR+RxojA16vXzlUO8c9KH2dJQ
E17PgZnu7/hECmQcT69ZJAOh78ILEXRYk+2ixSTaZRTtchJdzfXIQ0633O2Mi/7z
z5idQmQeXbitC3QIZeNzwitl2FLXPIw6MUnktOKNjQSNxMr9AU8q1cfOoICJQs0J
wYVpby0dv1z0f2N21JJywaeAImHa2h58sSX6uqwOXtzdwcPlr2+iJ09YwHM8uO0D
rtPx3bLHfkvbpMd5cCKjSeFozP1nVnlKJUmAZ8UXk4MAFKCtuLv2/InA8MkclZmX
1IzBbvYDOw7AnsAN2VQYZSgM8vCmnRCzpTmtvb3ysSo32sTUjGW44giuRtGqh5Ct
LeRpZZVm6zDFY6cjpr34+3Vc4pys81d3Dq+Sos4YVPXhTKW3I1VtFIdCeNyey7hv
epjna6/JvOzQuwK90+t9VmZk7jTY2WOUNXJhzDTeDku/aTMIeXUZrAxg3pOvl/wf
SN5i4Gauhl7URDx3nI0jc4Y2u9NGFi0TYJMCRVVLknAcre5cDKFyf9ts1gDyxTDc
+orszCE3ZQzXZeEY6aiQPihhL1YDk5SkkTI6XHmUqLW3HFlfg2aFH/oI+VGBAgMB
AAGjgYcwgYQwIQYDVR0fBBowGDAWoBSgEoYQZmFrZV9yb290X2NhLmNybDAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQsIzqwsaKX
aQyBeov7u8c7q8IEdzAdBgNVHQ4EFgQUjHvSRaHnej77FFWN8+DFOioZygowDQYJ
KoZIhvcNAQELBQADggIBAD4GmfmI5R6cy/Sp37buyO53azgw4RvVclIy/2qSPHVX
Os0pPPIIXLsbJMy7M6rDvKx/thZ27BDwms5dNuDynZ494XjqTmwzbBr+qEIzCNpa
QiX0MHf9JqFq5hkcZihfJ8PZL9JWIjiRfMI6AERd1pU1QJI/G8ha1vAkPfcA7GvH
NBBwKBm63iHYhp+zmnEEh85lpj4pEq+hOLK+mJxu55BodbNiBlBGu4EbalZlwXFp
7lGHLPkxuXZndrlrm8Lk+hi558NgNFxqz6qYtUc7txajViU2xFjEkTcQ0FKNiFpV
eHn5TNjwh4QucnWc7wI9hcDoADrekTEr8mUKrJXgxaBLIXEShC0ZDBnJsEwW4xbg
GQD9qXyo0h8cYx2NngvL+9Ee3rzdYAaSfAnZOU0xxrqZt+2dstNqi3pLKdS6RpA/
3Dt3cAnf0mQQSUP6oZQpfdSEJ9uggT9h+kgJz5RGOQfEELKA5RxHtTe7249J3vDv
hoy0Uy6+w3Ji5AQdn3G5uUGxLsBh/uS1dl8hq4gihrpPbKVJHVxqPL0HCx7DyhRw
hx53GXhGIgZN7QSGTrB5iz9YyzejsnysS0Um+figkxtC1atqhVkqzMDZBHUwRPfy
wq8PREXoRITJHfSkg01bInRFMMNWDQuPwSHDX9OYNqpkXzlV8/ao9Rx6JOtiG0a8
ggIPADCCAgoCggIBAJ9fTVNzE5e2H+WuvbNQZYItoFGBM6jnz0D9OMxonCN+nliF
8+7h4tIZzaw/8UMIE+6gFjSXNL2ZGJqH0QOQg20eoqWPY3q8BZ/FYzLih0NpluV4
PNuBRw/lG/vcDVk/ozCYvd1YKXkcPcQC6zFarWeojs3FsflEbQ5zr8LzGjoNmSEx
uCJt4UMAVrzZbld2m/lkV91UVw9ldUFxvSafyyS9FcKgOIlPiLWq5pfwqt9Ns9ir
K+S421JaFo39dzMYWaDQkCLI2imT4mv2hDLeS5nin6ERD/9w7ZBEQEdLLchHRaQO
nZYIx0qjLsxPTHxJKj9UIuz8dkGywFNF7+QAXtozr73tCu6Z1dUkDn7sk9rfyGto
2r0USqOHGfmqQSnybhKstX5YpSFAXh4NJUYbnZ0JgttlvlYALImHdC+HuBl73mSL
Dz+tkQ8W6U2kAMWT7M0LmXmuj+iTyzFGqg1/1URm4GStRdqVSgmp6D1jgwMDmeRP
9kyUCfiNXESfnIpuMtJzlUP37dgYtLU+TEvoxsX24Ea7K8mJ1Op9VoiI53GZbGX3
NdeUYpFvy5EJAy9N9KvIWeVttzy43ZRB7OayLqW/FWDga2CUTGzTcEeTfqQReGV0
b/8ZW/5SYCcocKKNp/bDC6DqvIx8SOoQ/AxkLHLVVLCzq6uXYQItMpnM39/FAgMB
AAGjgY4wgYswKAYDVR0fBCEwHzAdoBugGYYXaHR0cDovL2Zha2Vfcm9vdF9jYS5j
cmwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAU
Dz/nhP/+DIPql2WnuHiBw1W1pCowHQYDVR0OBBYEFEGA+rRYENEX9lvYPjCkBIlW
Y0vlMA0GCSqGSIb3DQEBCwUAA4ICAQDca7YS2ZH1bjXMLCS1fJ7cBt680O34rppR
fosnvg4eI10G0cTJb2khXQlH5vvDfEWr2wz71TvgygsN0n0tEAcbU2J2Fgy7J5Eb
zv+lkPNcYf2bwRaMAxi9ARxlZSoz5IcLzDyD+BcTLAGsMFymD29Ofs4ej8x/t613
KZ5lCiJ0f+UUuNZ/U2e9dAhr8m2XI4vzhmQyx2NO2GfXSQ3YconoAflK5QbVYaXt
QQiz1oz4LOMN5y5zRbg1MekF1XFITsGan4rs4Zl2EDZ2rge67Yn2RMvmLyPUxRTY
+WX4nhRiIPM8dyIQb9tqSjTDhlLanWm//Qb2qYW3PNtAAtADF4opFBysX/yJEJWM
BIxKyvL9S8TdlIIlG4D6Yigu/n7j1vB6aRdtpUzrKMqoktkw8xtV3HIs+uS1XPoT
xyL4xC8hWePMmsIvvTpxBtEEZSCXYMFp56QvR82Lv25ORfcLCZ0B+CA41AUiHhvp
1Vloi6e9d6fMI1aOpWtiC3+tJYu98LAylGBBq8bec1h0rejpe7Qe7uPOeBL+SFKD
i6mQw7fdbv8Bs8+wjYqvfSe9G7BPvaccyC+GnzbP8ElrpTPVfLT0eQpdtftxdCH+
Y46lz8y4ixMG99vF6TQ37ca4Cz52Pw+IN5JESkWEDphqO1++YkytP2og9dw24Ktg
ZLaX284IyQ==
-----END CERTIFICATE-----

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAQNfN5BLLLAdhlwGk
Ve8w2t+9x4LWR9st6CLCYHnTgc2gr4+HqgcMwmNuj3cA8ENBvIShUnHkX2E+9CyP
3W1ZN8OhgYkDgYYABAE3KDrdCdVeBV34NicA8AoP7hItcwxgXHOQF+V02N6b5AR7
w3YDHE/JSzSoZZIYiqdy8SmeD5GtwHLV8tLQ8xnadgHT0BesGS02w0H5foGzzZOH
ZW3AfbdH4MpssR4Bf1jqL0jw6eV+oiMStDuZ44zri7PFjvVByt6MoeSvh5qAXCQG
/w==
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBLVCOgktjIJCL3ZpC
vX2xQjXVWPiWaWxbx9OGPQArsCwbAtJ+Oa7TDaJRRi0EHEJIs72ZPhozNRYppQoe
IQPiqWyhgYkDgYYABAE69tLPv1D4DIFTgQo+P57ZABDoxe6x5Rjy+NYiRi/Nfir1
1CxVGQDaYDfUFsb4peQJz6haxqXfXmm4MWlIbdWa1gHMf3GQl/xtIGGbJURxnBIR
3rj2pqd0DqbONj8blnQTCWLSHsLsgyI6WWK4RHd9ZtER0UQDxoQcc8Htsuh1+SKo
6g==
-----END PRIVATE KEY-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+TCCAuGgAwIBAgIUWlKUd1HKU8R7cjv5NJdWhHzhT5cwDQYJKoZIhvcNAQEN
MIIFADCCAuigAwIBAgIUU+2QY0/U0dID7GqyyHWsrN6astkwDQYJKoZIhvcNAQEN
BQAwgcYxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWdu
aW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUyMzlaMIG2
aW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3NDhaMIG2
MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBN
YWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxDzAN
BgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZswEAYHKoZI
zj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJFhzrwY+wQ
acmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvLJ6lKJuCo
g5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qHPAD96UED
M8JanKNzMHEwHwYDVR0fBBgwFjAUoBKgEIYOaWJtX2FybW9uay5jcmwwDgYDVR0P
AQH/BAQDAgMIMB8GA1UdIwQYMBaAFMOMHl201gGYrA/OoGUroGeldRTZMB0GA1Ud
DgQWBBS+JuVMVW1HRJfcoOLI4lijvxAMcjANBgkqhkiG9w0BAQ0FAAOCAgEACPbD
hFuibNamlcpnVG7mgnO56RB/K5otOCGS0hbR3OFUGY1ZDQ++M44OF/b+eNYHgjsr
ER4r2cne3qBjOArFj/toEDGM2l/DFWDnpOvh1ZItJjpQe07OGn+KpTLf/ZB0Q5D7
jV/ddjJ0GGyxessjfUvmCT4BVWn1bXKJFSgujic7lgMf8WBGwW+WW3eZecYsh/Cn
rHWfQYqtvzB1uM89bwZwN+lvHz/QHTreAShDAiCY9M31cemvUvC58z8jPKngFhuf
C7ZMUXbzu2jYWo3EowzvcRpO1KqlLfNHjGLbaBaZWP2ocK2IUTrsAbr7PTdcAqjX
TRIds4JlbidA8OziEXFTcM2xShS6WISsO+9JXk5Xxc3+xscjSamHTMwRm2LaKjrr
d8mOxMs4/5547CBsBFMqu98tiD18K0yqdJiJJRDDOvqVeCvDE6y/pzKaSHIIyA0P
ATnjQzvgqbcwLrdoJ4WNdTpZieoisUR0oMAKUViPwd0xStzNX6K89PSd5c0Bqbhj
TwCpaT+DfcvlWk4wqEa9rLn+Su1SUfMSD2dITynLW0UexiKPHZ5epOPu5ho6cOk/
fe/N1cX7SyOjhErWLwqr974YM0EPiR6f5lNooYXMPbatq8lcG0gpEg5pggHhsLiY
LGfvu2jQJKGnrBYM3IcRGUsc+VZ6CNTJjaIIbGg=
zj0CAQYFK4EEACMDgYYABADmnrO8fwhFRmvTAeT91LiGJatTUNRhTr+SPf9glpJG
Femv1amiV89qBYij6IuYJDhSYAQNllegWfntvzDbCOhDvAH0SY9Ivhd77nA20M0Z
9FuUNaFBRAkUB8VhVPC+lMM7i/0QA3FCWQEP1V28oXSyIOO7SF77GAb8U8dobSvr
Gtb7dKN6MHgwJgYDVR0fBB8wHTAboBmgF4YVaHR0cDovL2libV9hcm1vbmsuY3Js
MA4GA1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBT+K/WumDBi4hRISudTeDt/ppiG
7TAdBgNVHQ4EFgQUy6bpiWFK4+V2aXRfANx1B/CM98owDQYJKoZIhvcNAQENBQAD
ggIBACFCebna0DLGqzp3DYY3bdOIuCD5qCfYHxEB0OCMeOsFT3Fev+nDKijJ9vqI
F9yCKE9ddWNygNZ3GDMFss1GjYcMeCecvy8nr3xiR02V1vLZxjlgd0qwSGUOg+jE
trumFLoURc4xB6gu9F6vaN6fW7mlYJtOjHLtcKf226WkYYFgsrD0nhzGYosqeFRQ
JMfLQAmqfCU1z2CwADWK8SVZvAqOY8cdtrXnrvO4fM+lDmx7ZhGeAfChq8bo5EJP
pSjOjlgZx+m0teCJgmWvRIeq1IM5ntaZ6/BArMxlhUYuA/g9AvW6xnHuFT1yy00z
DXe1aTjbBS2/jo3Iaow99Qf20bthm9h+kpaNyaBkSgWrZewhYc7TfyWRKQ72x4Ob
VQ0nzpt5uRSTXv+BGFk8MzDj20pxxa3C6j1skfEp3Us1Z5VJjkXf9v2039bOehEv
SV2w01sFeuqMLbyiL6eXKIItn/SMy6Vq9muwdr0XUwlyp4Hmwiws9cJC0b7Rie7M
7TiNLIviCRumV2JM4As6zS4pI/ljBXEaKi16SQnOERNYJx6QzQy8Uv7HKREHXa7P
12cfpjA4Xw1RhrLpd9B9ZQL7AS52cWiKqNI26pR0pOuf5EEN3oour0byVoYN7FQs
ypmaGcd2dEJoytW0QnxVrcpjtU4OX6YMFGfkBifsTSNy/lWz
-----END CERTIFICATE-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE9jCCAt6gAwIBAgIUSpwJMAovpO3Z9SuY7Zw+/pv3bLEwDQYJKoZIhvcNAQEN
MIIE/TCCAuWgAwIBAgIUJI5RArOzzLAWps20JDkSDSb8YD8wDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwHhcNMjIwMzIyMTU1MjM5WhcNMjMwMzIyMTU1MjM5
eSBTaWduaW5nIFNlcnZpY2UwHhcNMjQwNzAyMTA0NzQ4WhcNMjUwNzAyMTA0NzQ4
WjCBtjELMAkGA1UEBhMCVVMxNDAyBgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5l
c3MgTWFjaGluZXMgQ29ycG9yYXRpb24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwg
QnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3Jr
MQ8wDQYDVQQHDAZBcm1vbmsxFzAVBgNVBAsMDklCTSBaIEhvc3QgS2V5MIGbMBAG
ByqGSM49AgEGBSuBBAAjA4GGAAQBxjtDJ6UGukNiMYKlewBsHdzsAZ2DiY2CRYc6
8GPsEGnJiY1njch2oQQsbuGzPLw6+9Eheb2Ra1qXFqXPSU2OQGoBQhQ6iH0ryyep
SibgqIOUXzLFiaRto/zJyuzMRvj4dEfiLZixW1hcQleWDk+N6JYcGGa/6XfahzwA
/elBAzPCWpyjbDBqMBgGA1UdHwQRMA8wDaALoAmGB2libS5jcmwwDgYDVR0PAQH/
BAQDAgMIMB8GA1UdIwQYMBaAFMOMHl201gGYrA/OoGUroGeldRTZMB0GA1UdDgQW
BBS+JuVMVW1HRJfcoOLI4lijvxAMcjANBgkqhkiG9w0BAQ0FAAOCAgEAd9NdTLDK
fWPdOg78c+CaG2XVnIzBy6niur4vM1DPJYbwabTYbpJM2jtevMo3h96jCd6AfHzv
cFZ1BzpYsWtjbDFhiqowmKnlFlOIoIDXCaG7vBvDWc4iWGG9PHMWEouSQWxg77bZ
A8l7+K0VdRWYCqwxxClK8oK3PqGyGqELRDWwlBb0kRi9XRmB+2BjOdZOQvmGka/R
l8hZWHTs+SPHZ+ySiJn55/w7gN/LCJs1XX70jPrzu01lDPTqVIrGKBtWBMKFKR7g
A4r3EThfj85Xq1T45rvD0ozlkyVmdMtSk3z+fKCt5+qEHAu52kq2Ps7xQviAYkIK
f+yNf1a/Ly79QoqEibRXuKMk/oWoqv7f5vT5HqoWEWZFIYbdbYTBIwkw3mHwZRZx
CcyiukHw2W/ZwM6p60gXWZwrQxwVkdhxWnAHrAToL6O8M9Fk4aVWUkFNjqVUjTWS
HLCxv8mCPujXIneqXyiJOpAPKu2kjmoFwfisjevNUmO5EqUHUba98Ne7rdiWweP3
nwcBBKIQ2MVZ/lPqfPtqFOHqBzCbqozVw1HzE2/ImnYsK8SGqwBnSJyTt+WjM/8S
4ulkjVDIwWFwUkOWjM2yorp3u2tFJF2sMv+3AR3mp6aDjF7sCzYpLaZag6oDmwOF
nR+VwEbTC8gnl21swWSjtUC8I0EidfxzCpc=
ByqGSM49AgEGBSuBBAAjA4GGAAQA5p6zvH8IRUZr0wHk/dS4hiWrU1DUYU6/kj3/
YJaSRhXpr9WpolfPagWIo+iLmCQ4UmAEDZZXoFn57b8w2wjoQ7wB9EmPSL4Xe+5w
NtDNGfRblDWhQUQJFAfFYVTwvpTDO4v9EANxQlkBD9VdvKF0siDju0he+xgG/FPH
aG0r6xrW+3SjczBxMB8GA1UdHwQYMBYwFKASoBCGDmh0dHA6Ly9pYm0uY3JsMA4G
A1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBT+K/WumDBi4hRISudTeDt/ppiG7TAd
BgNVHQ4EFgQUy6bpiWFK4+V2aXRfANx1B/CM98owDQYJKoZIhvcNAQENBQADggIB
ABb7ufyi/LVMefLjFCfUpf0iiCuUq81DYKLI5AZ0Gs+Oip0OM0FfADCz2Gyrm1VR
97wK1DLRJ4fFFEE5EOL+pNVGjMGUAwmhcR1xPAhV2AJ7jD/xYNZmdnFjQE060S1n
NFOZP9WLyf4jf+aFUTKxpmfRej2Js3WziqRYYEGODmoatIeZtcMc1/IZnxAxntww
sDl8jFCnnzUPJELMh0ADFBFNUpfnlXKG3W66rMKzKAHAcl96rpWWBOEZ0i/1oGuz
l1/P8DzlJhqR+VJMeX7Ume9z0/ZUy3Zy4puXAcflrx8r+vH4BDCJsebuEjJAvgHz
uRor3WgOj+3w3do+pYX0eqaMCCJ15o0X9TWnR89mUqYav7TeHiim06vnpLSgmK19
1Lvj91l0ua3BSgqWAQe8rwBxvFOg9oC5Kc+GsCPunJb69lUskaS88foA8PZ5c/34
tSMj3jXcGAXY86qxAfDjejWR+sF7uVW3Fqr/RKJ12TzPYVnwvzvio6BeHey6Eyg/
4EWHjymQwfPpyIYpKp0nYvE/PfqT46sFtVA74Z3nw/WxDHUmo7l70X+LfU/TYi3+
qUyomwxSBTuWxUtBcNiTfMk/8yuTv8D38RLdpMUuE9Xbfd4DLO9OSgMLrkJmPvLc
r8Km9zHV0N5/KA5EyewrPBsClDOwN15bqncbk+RG8hUL
-----END CERTIFICATE-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIFAzCCAuugAwIBAgIUELm+ubD3lNPrFxtCj86wMh7lYEcwDQYJKoZIhvcNAQEN
MIIFCzCCAvOgAwIBAgIUfBiRwLX9KVgvBmm8F3oCdB+ai2AwDQYJKoZIhvcNAQEN
BQAwgckxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMSQwIgYDVQQLDBtLZXkgU2lnbmluZyBT
ZXJ2aWNlIEludmFsaWQwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUyMzla
ZXJ2aWNlIEludmFsaWQwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3NDha
MIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVz
cyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBC
dXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsx
DzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZswEAYH
KoZIzj0CAQYFK4EEACMDgYYABAB/2mDXj/QP7fcpxLTNfFfuh/X1Y/RS8XK+Y4v+
0nisTQhe3MK8BZ7D0bcB872s8EJCYZ1OYr5O2epQgv2hbk4T1wCGKe+Pjf2QHTts
wXMpPf87R3etF+zWR+CYH+5N5uQhjW9Ueeq7wve3A61e+Atz7IzTKVlM29kT0XvI
sYtazn6usqN6MHgwJgYDVR0fBB8wHTAboBmgF4YVaWJtX3dyb25nX3N1YmplY3Qu
Y3JsMA4GA1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBTXVfLQH2D0KccIHhK6M5MR
TITvtDAdBgNVHQ4EFgQUWdGEEpvjFJ4SNUillC09iBfcTEUwDQYJKoZIhvcNAQEN
BQADggIBACfZ7+KtT0Hl4Y/JBwCtUqCRRt4kCM5rXqORswkJ6RNmx1OtBHPlkUEI
E5HrE0dZlaECfqMkOBxhbBqOSpoL/xjz1gDHILWXEbWchuFnU4EAgYvJS7nLAHHO
swJCEP8VDM3y5lUWni8sXs6ro/H5tAkm260+xbn0BFF99l+M124ZUawB+tDg7E9b
MNG0eWtfeiZzWBBhoxhQoD03gFADWxTU1EK4JgCl7frsj4PjK7TMKTV/Gd/Bd5l0
KreFW5orupd3YBqDeUYI6p1l9+uThottwrA4z/POygxvhqgoqNA/+evMWX3wxNza
ComAmWffB5EJQFEKcpdASwEhyjBMap15GdJVBFGYzXAlksQz7k1fr4h0ebuYtcpb
dvcDyeIxjsg5XiP32494wogLUjHioi5mwIrGn8/9V6JLRGfKBHnmejHBR/cbMpuv
rpl0ZZks3bicmBVTKFyj1ORRGX1cXsbtCbPBJEmBeR5IOFYBeaQHrqTcaEBz4lWe
K2OT6ebcqtsCzDSmjsoeAX/CWRVaIzLzMc9SqE8lZ68ajXjbt/59I7EBeOenttO5
mg9mokwDqoeM0bXyT3qdT4s9fhy0BPgnBqJHKIc9afzV79Iz1uJ/E+TZJ+tzhbAU
n4fFxIHOdbyaOLCLT0+O74FZCQq8FUUnL9QVY5dXTH2458IUiD20
KoZIzj0CAQYFK4EEACMDgYYABADRQ0Xq0/NUVMVazLQK2oZ7TbJdL+ZtXeQawN/9
j70DUpufvfbFLflnfjCD7dWoMGZlAV+pfrIRB3fI4YKvzGWdEwA/PgEDYpxS0onE
XtdAvveL+3+Bb8f3s68z8+UH+fDtIKZRLen8yDvEua0WpPGPoE8Xga5ZOoAJHWeO
RVsPGypz36OBgTB/MC0GA1UdHwQmMCQwIqAgoB6GHGh0dHA6Ly9pYm1fd3Jvbmdf
c3ViamVjdC5jcmwwDgYDVR0PAQH/BAQDAgMIMB8GA1UdIwQYMBaAFGI+YnrDs4Q2
f3a0zSucoJYGJqYnMB0GA1UdDgQWBBTefgLJvMQh0wA+A1PQV2yFHVV7vTANBgkq
hkiG9w0BAQ0FAAOCAgEAQflAXs9mWGhK5WXVYzSx44S+C+U6eab3N9NCoYQXOv14
z96bxRi+J8yB91cZ5Z+Px4gtdzZihNx8fIoGN7aHwNzfBuRhCYxCOGRwN35MnIk5
sKXpvZ2hVIvM7SQ4HMujJPjfUesKNWXBGTILe+nXf9KIruznd0VcFtbAEkpN0t5v
z/L7zpWOI6R9lYGgaEzfHn2Bw8lm28rLP0u0QrOfUcJiNcnVvhUfdfHvBLzchOF4
A3mIO8gl1VRNacxmB9ZqpVML2hck17oXm2wmF8oAsXMPjOBk4uv/DrK5EZufByB3
yaQaL3gunxBiEQs7vAzxVN3IpoHuXWzeWm39ttgVKDRu2URUhRILS6sK4Xar0Xoi
CPvf5tOsdkTn2+OzS9iDl5JiHVGkJTVa8qAd/wDZ1Zq2SrECJM8gObp0Aom/JH0z
/NlJs+W/CCMwDhV4hVyJ/2Io54AaOEeFiLaBtwR/Tgm16B87pAwgRqmWbVUIJbIr
Vv5hz6s3SS/w/KnjfgzZt/amQnEOLEqFDUCFb1iPPX7yor40nJU99b9GxknuOtzG
1zMxJj+9OMm3BT+2F2+BYF010uJG4d8CmbOf6nKn8x6QBQN/sUfqk3pMnVdx3Lg4
BTScZcuPVEZ2hqAHOiVu7U544vALW1HLQCcN7iB2T7DIjIYsY4dhjkgQwezoZ3A=
-----END CERTIFICATE-----

View File

@@ -1,8 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB48Wl8pvlD/o6F7YG
sFfCXNQRbKbppIrywEBkfNG0VzfXazBOLD41bcdPEBh1111kSYgn46uUVxjabyLp
BgQthf2hgYkDgYYABAB/2mDXj/QP7fcpxLTNfFfuh/X1Y/RS8XK+Y4v+0nisTQhe
3MK8BZ7D0bcB872s8EJCYZ1OYr5O2epQgv2hbk4T1wCGKe+Pjf2QHTtswXMpPf87
R3etF+zWR+CYH+5N5uQhjW9Ueeq7wve3A61e+Atz7IzTKVlM29kT0XvIsYtazn6u
sg==
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBTCcA3Wwdesq/meh+
lZDOSRex3E2nJawTEFuCoOCI/6Pqf1jw/LjkfzVcRM85vQ/E+MVH+j3mL28nlYNW
Mxl8VqOhgYkDgYYABADRQ0Xq0/NUVMVazLQK2oZ7TbJdL+ZtXeQawN/9j70DUpuf
vfbFLflnfjCD7dWoMGZlAV+pfrIRB3fI4YKvzGWdEwA/PgEDYpxS0onEXtdAvveL
+3+Bb8f3s68z8+UH+fDtIKZRLen8yDvEua0WpPGPoE8Xga5ZOoAJHWeORVsPGypz
3w==
-----END PRIVATE KEY-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+DCCAuCgAwIBAgIUMi8utJhRyr5bbvABnCn/Q9/vJ0kwDQYJKoZIhvcNAQEN
MIIE/zCCAuegAwIBAgIUCpd1IYj2RH4Kc0Pi6+ZzfdZv7n0wDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy
MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3
NDhaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAHkrwf4hZ7M5ahoYHT0u1Xgl28qxURcdNmW
kYDA5u1Y2mGXQq85BakAOyfxl/FF/cBrLm0eLVFpePumqgPna3El1gAs9p2SFwN7
4MmcLhbwPmGnWmzVrOh9cNS988XYf01E74966r4MKpAGxSNPKDwu4doXFVAYRH/w
tcgbjlk+Qi3c96NsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B
Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O
BBYEFG+41urs6GhYzOrJD/MJDWMSrUlvMA0GCSqGSIb3DQEBDQUAA4ICAQAnowYb
R1qgnpH5FLgkj3LndAAfFmJMlTrNcmtafjaLZHUmxxO1tFjb7vvGgu9LggwwSRoN
y0Qsu0mywXd/ntEuWDyD735aGtUE9xnmluoj4ILBnln195VuvUoWYjCy0XsGZg6G
UL8bOaZgbvXQWDcdXfpYnM9C5Uj4mB3LFDdasd7nDv2F9vZlHNkd0uGmZvBco3SX
GNFETOaY5HjU3jLlCmD74p7G9e//+153R2aXEww3SVVJyRowO7SpwH88DNjl2a6A
OLWoA3oRrX4rDQI66LYv0RZMDCTvzY+HxOQHXeswosA8D96GhpClb1X0wLZUp6gR
pLzVzaE8RFnSg52jzjI3ehFIoSK0x9R1KPzKbYDh7Q8Sc5sOSe6r9Q01BdpBOUjp
iTKhgbUEbcIwcaKLIGeSBe0+Yh1uQpqBtOPectUp9bz+hEUDzG6QMS5VFxAGuZWY
2iA97jxHsUBEgMBZha7z/hQ68Pst+KaIV/24By8lKm0rWpLSKOXVAW1lWZrlpIcx
Le8+dmy9/JFJXmmN/pmAEVVsK5dYtVXt6ikVi+LPr/7fCfvt2zk88wiUp8iz5jgQ
pNYT8MPOMf+yzzoqPXjGmrT5znuHnCg0OSh0u8NILWQPQl8M6KNiI8lzcVBy6Djq
fmnQlaxP1Ey+wrznWmBu5zesU8uMyRTxIfnEzg==
EAYHKoZIzj0CAQYFK4EEACMDgYYABAFOncuQfjViGAC/g8Nt6B+11h18++MD1+bO
LgZwk94C5u8gntb0I20QqeMFFuqmNMouFk6amimv64J6dNnaFXVh/QHOTTjZugyt
p2WB2SpySjRIbtOsG3HFrAeSDJNr+a87NcMzq+s6aRdv5krVrKcSkpQrPnxVYV2Y
oc1zEdsEyw6M0aNzMHEwHwYDVR0fBBgwFjAUoBKgEIYOaHR0cDovL2libS5jcmww
DgYDVR0PAQH/BAQDAgMIMB8GA1UdIwQYMBaAFP4r9a6YMGLiFEhK51N4O3+mmIbt
MB0GA1UdDgQWBBRSJBCuVdARV15CwCDKg4XUkfQfITANBgkqhkiG9w0BAQ0FAAOC
AgEALhMxuxt6kmvhBUVbxkwBS+6W19SOPhyIGbgWpiZabF2WcapQUYDwWqovswZ6
YIR1H19rEThXUArpMUy2PFk89zum1WLdK5j3URNRptyCFOTP+485fIpkPnDURIQ7
PWcTAL1pPmVZpXyNWem+Yolwmkt8vA5tGAopnbp5gcvoN+bfiDr7kwOUd958p2yr
xwDqaRBxzYCniLV38wrcVVMK0fWS7L8SmV0cprh4FBhsDdLLFgZAiM/Bex97soPo
nBRHLinB1I6VoRVWikBkCnTsD/i9ofqoFRcvZRNcnWgM3eNi5e+zoBKBSvLW+xcS
9sjNkbneWgTg0cKGqkkXwG4gccf4G2K4s6x64lo6Tkukp7q7uAvN6KVj6r2D8ATQ
09TAgRd6EbMbZCXC4kFZweCbpCoNubypXIuCCL5uZyftT2Ji+r/WFE6X23tUChu0
7B4kNyipl15tNQvMc/6AEZ30C+QlPd+q9EyFJfajKriOqrwrfqFutfOVqKkFC6Mx
WSvTR8R/kwLvXfSgwWDfeBesULnOs8GryrSQIlzoT2eUxeB+J4qxtHavIH9Z8vBV
anT+TdboVO8GrWtgXBr38WtY8xEsvMcYMxzHZ2GcJ6V3qDE+4EcbPl/FuwFck+gJ
3feiz9/iV1ajuVgLhvX11JmR65sbQQrLPFHm+vm7Xv0n0fU=
-----END CERTIFICATE-----

View File

@@ -1,8 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBLz0ExeBJrxd5gA7C
YFJLVuNd4CPNc/jsOLIMnIbLHSegdOtQn9tief97k5rZdz/MQzWp7rCmsLUYw/fM
ktUQjJKhgYkDgYYABAHkrwf4hZ7M5ahoYHT0u1Xgl28qxURcdNmWkYDA5u1Y2mGX
Qq85BakAOyfxl/FF/cBrLm0eLVFpePumqgPna3El1gAs9p2SFwN74MmcLhbwPmGn
WmzVrOh9cNS988XYf01E74966r4MKpAGxSNPKDwu4doXFVAYRH/wtcgbjlk+Qi3c
9w==
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAK7ON/fwW9+CMylco
dAlDMVp0PeEPci/5GxVkb33rkUVct7wRXceSKg5N19L6+o7d3iyKt1G4Qm8L5cgq
FsdQxYahgYkDgYYABAFOncuQfjViGAC/g8Nt6B+11h18++MD1+bOLgZwk94C5u8g
ntb0I20QqeMFFuqmNMouFk6amimv64J6dNnaFXVh/QHOTTjZugytp2WB2SpySjRI
btOsG3HFrAeSDJNr+a87NcMzq+s6aRdv5krVrKcSkpQrPnxVYV2Yoc1zEdsEyw6M
0Q==
-----END PRIVATE KEY-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE/TCCAuWgAwIBAgIUPRltRSFZSbucmw20u9TN7WHxG8UwDQYJKoZIhvcNAQEN
MIIFBDCCAuygAwIBAgIUNuVS85z9zeYfWs6GLZLJ20TLASYwDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy
MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3
NDhaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJF
hzrwY+wQacmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvL
J6lKJuCog5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qH
PAD96UEDM8JanKNxMG8wHQYDVR0fBBYwFDASoBCgDoYMbm90YXZhaWxhYmxlMA4G
A1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TAd
BgNVHQ4EFgQUviblTFVtR0SX3KDiyOJYo78QDHIwDQYJKoZIhvcNAQENBQADggIB
AGlM/E6RfiQV8si38/IieHw2TsZDNKluXA/edGN4d9i5ztIB+G57IgvAR9GBeRTB
AxANycBGOWy+VXsUij3UgpoK3NiZJ+XvgFYqx0ZVUX3uPmU6R0Ko5cHGmSEVV6KS
5ToGLqSZdftybGt9NYKg+LGxzIhT15tgUENkOrFVRSlKVn1zAP+sa2vOEtH5aX6S
ins3hHQBuhGKSR/z2I5IzEVhnx0FJxpFO32QBdmXUdgjlHcpiIy1BaQRTca/U4iJ
Q+fnzn5UP5v29ftr/PRh0W9rPVeS8ge+vuPC1eriRL4WWnkMFm0QljERqhg48NWk
zT8mnVn0E3vQ2Y71FKg85ov0Sza8/OpBgAgnoyCDUIp8YD7BrU1viLEoqLtlvsoH
l/+mU/CnCe5749C6OBppKhdrIRSG8bh6mIeco1ALDNIaqGoUtdhib0f+YKeCc0zC
MFjvoSranndvwO+z/qdL7Feay8uAjgq1GPNuj/xaEbzrZz0yF2viMXfK62kgwq+W
ebg2D1FHVuKXQ2q3IXmwcyLOEUScUcEYDsM5eDenoScgIE2FfHvoGOdJVLsQ9U+8
2HTb7ntvbc6ANCTcnt51s5/PfD7UaHeJiSxRpU6rjxnqKZ2mEjbF/Ha8AjImQrrd
jpjNgH6H5p7ZQqc/4DGa/Q2B/rfvmn5bAgOhicEKMzm1
EAYHKoZIzj0CAQYFK4EEACMDgYYABADmnrO8fwhFRmvTAeT91LiGJatTUNRhTr+S
Pf9glpJGFemv1amiV89qBYij6IuYJDhSYAQNllegWfntvzDbCOhDvAH0SY9Ivhd7
7nA20M0Z9FuUNaFBRAkUB8VhVPC+lMM7i/0QA3FCWQEP1V28oXSyIOO7SF77GAb8
U8dobSvrGtb7dKN4MHYwJAYDVR0fBB0wGzAZoBegFYYTaHR0cDovL25vdGF2YWls
YWJsZTAOBgNVHQ8BAf8EBAMCAwgwHwYDVR0jBBgwFoAU/iv1rpgwYuIUSErnU3g7
f6aYhu0wHQYDVR0OBBYEFMum6YlhSuPldml0XwDcdQfwjPfKMA0GCSqGSIb3DQEB
DQUAA4ICAQAgTk83UQt1kM6rk4/HZ6o/pJ5coLDfmZYtOxQ6OmoDzy5ok59iTDsk
rZIjNSnX95iKsobwYbF9K4yp4d3urJjSIq1kVKnsK++fYeRmT6G3rAyF1kZaL5CX
dWnm3qE7/p/SSmwK9Me3IoNEzPVCVroBKOUeFTuxnTQyXQUXx4ItEQTpj02g0XxK
PvPs8rYy7LGAGUwQqcBpBN61XvNX9DCUa45iyzCEcgFC3gMlBm/Cr0Um+W1Rh0Lw
ULq0Zn8ZlgOLS4EXTim9R14RWVUh7EafunYG8Lxh2uoZAQo7QFaxz1/9SJdlhWpQ
NsxK4hEDjqM8xcKXf+gRPKXMkFTjgMmH11U6CuKnB1URDb8oy/pLlJkJR8BcnR3v
BWoezep9j8LiKj9Jv5P8EM9CAev+VdjG9ftnME/e2V4/3yfZnKj5uw1Dow/9fR08
XRL7svrNSPcBM6uPq0vqZzADGizoMGmg0D47Stb9NgMi844i6lTCFPeVu7r9OWhx
+nI4ikeIKgbZNiMUl1SUEbqkWtCHK4EF1/NE3K6M64ATVlONCC4oU/ko6YtFZVk/
zVTVGUf7FawrkrUbojVim4t5YyBL5rtoU5AyvMhgokpIP9bpWQHQhwAWZAmnKXBu
1kBL2+8uLUmsHcnuZ1H+f3y5l2XMH/WYj5bcbhMoOUm6vY7FhXKOgQ==
-----END CERTIFICATE-----

View File

@@ -3,57 +3,57 @@ MIIDfDCCAWQCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx
MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzla
MCUCFDIvLrSYUcq+W27wAZwp/0Pf7ydJFw0yNDAzMjAxNTUyMzlaoCMwITAfBgNV
HSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEA
hMmvyXLOtDG7faP93ZqMh061Qms/at5uYz/eAA9CGfS4E+hq2uQaic6gEMWf5NkH
9xRWB1UM9sLR5Ai/Fn+MqsbeXK+1ommOfYx0KL6J8JihHAlQpGYJXu683dxIB3LL
ijZ+od5pes5ZIRKtMNaO4+meDG6e5guXDeFNyJaNYqFXOJcy8OIL4O3T840qY1aY
YcHZs/sNwC/hSAloTeUQ6S+ga/EplzSDumz5VrRsEhVd3wScM1FNeqwMvA4VPUIa
NOtZJNLJBLhfmetTeO/wb/y3Ekp8sXAf8UJqABczJODSaGc2RwULmxJd2Q5lESpX
RkfvObqTWgpzpyV/1OhZYDUlXJqiADYnyAsQL8WV2uFQ5fRG7baVDiNYn4uE92Of
FN7jXmT+7UcHSO5L0VqHS6Ia6j2NammKlbhsAw7dU3uxEgCJvBYlWSY+z9Fs+O1B
hNU42fL5W7JnoGfqG+LqfDdbG0MG+GMo1rDDcvrEK73F41w7mLmZhfYAZY26xU6Q
XyLcrG4YfR6FCyOs53zwrKKG9x3ETqY3S0DSSOKTnuvIGZBCSOxGpqdb7pQax6IJ
PqsM+IzFC1C0Ol0eX1ee734rCHfNP6XEM7ucjBweNUJRrU0Q5CrGGJzEQz0ym/JN
81f/kQJJosSSc1+j8iDiaKkUgcQ1DWWZLYZgvCkwZcY=
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI2MDYy
MjEwNDc0OFoYDzIzOTEwNDA1MTA0NzQ4WjA8MBMCAgIrFw0yNjA3MDExMDQ3NDha
MCUCFAqXdSGI9kR+CnND4uvmc33Wb+59Fw0yNjA3MDExMDQ3NDhaoCMwITAfBgNV
HSMEGDAWgBT+K/WumDBi4hRISudTeDt/ppiG7TANBgkqhkiG9w0BAQ0FAAOCAgEA
fxBxN6giXdLaG5lWLbigI78WQoEzOxWeYbzUpqmn+QFd9YiJCNMaGz2XWl/MBZEl
gmpHNOEzOM5Ze5dRfbLDAzBBFghNGrDDQURMw0ASZfHX6WsL7MOoBqTeg4xbFESk
dXT9YCH/wS5sn5YgkKDh2jqHQya8c4TtWfuWYW/Anyo8wVhgcYAnAiSJZgSUciq5
964Ci2v4pifpQBbpx1MAaOgU7kkY59v+AHhWfxJ7whJra8DA+v6XEim7Y/uABW0N
Yzp+rcSZdDanFq5OC8YE9Kn0c7bHvFi0bDf/AmOB9D64OHc3PRQInubN7aNaQ89N
T+wgFhN2yMSuh4YB+RJ3C1aouGszzC275vYEcc2Iw2OdQQkTu7dhhV+pEfvkhh4x
myBgJKEBqidXDAe1SjGtNP+G0pF6XcQDc3lkSsw+btW1YjQIbv7uayuDfz7aqbh8
vPdP5QKWocPkIOL8sv4JFiETfVWqVMH42vE+ixC3+enunLxqJAUxqMZSfKuCtPYb
PhnSuZZ+rTfXpFfYZMMUTMhrx618+uttcQGosdYNv9e4YSR8pLvIPFef/wfWqpLL
Yy8xYSGOUJ9f8vfh0aIkcRiLbjSgnyJeSlQVRnF0w4FeTjm3QeY2gcdMzaIo3oyH
G23dJvzGq19Y5h4ShZoACuasTIKtaxLm0gjwCYBWZO8=
-----END X509 CRL-----
-----BEGIN CERTIFICATE-----
MIIGsTCCBJmgAwIBAgIUMwTHYM3peBjwVRi6iICr6FVn0cMwDQYJKoZIhvcNAQEL
MIIGuDCCBKCgAwIBAgIUMX4D3d8lY30ZIeB/PpAi4vEPvfcwDQYJKoZIhvcNAQEL
BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg
Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHMMQswCQYDVQQG
Q0EwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3NDhaMIHMMQswCQYDVQQG
EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD
b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo
aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv
dWdoa2VlcHNpZTEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2
aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PV
OTIAvzTuo3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdq
ZR3KFJ3QD+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5
QqODD+GU0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2
cTJ5g7lokGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXi
t3IAhG60y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWOD
mYEcoVkzvrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRn
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
-----END CERTIFICATE-----

View File

@@ -3,19 +3,19 @@ MIIDfDCCAWQCAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx
MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjA8MBMCAgIrFw0yNDAzMjAxNTUyMzla
MCUCFDIvLrSYUcq+W27wAZwp/0Pf7ydJFw0yNDAzMjAxNTUyMzlaoCMwITAfBgNV
HSMEGDAWgBTDjB5dtNYBmKwPzqBlK6BnpXUU2TANBgkqhkiG9w0BAQ0FAAOCAgEA
hMmvyXLOtDG7faP93ZqMh061Qms/at5uYz/eAA9CGfS4E+hq2uQaic6gEMWf5NkH
9xRWB1UM9sLR5Ai/Fn+MqsbeXK+1ommOfYx0KL6J8JihHAlQpGYJXu683dxIB3LL
ijZ+od5pes5ZIRKtMNaO4+meDG6e5guXDeFNyJaNYqFXOJcy8OIL4O3T840qY1aY
YcHZs/sNwC/hSAloTeUQ6S+ga/EplzSDumz5VrRsEhVd3wScM1FNeqwMvA4VPUIa
NOtZJNLJBLhfmetTeO/wb/y3Ekp8sXAf8UJqABczJODSaGc2RwULmxJd2Q5lESpX
RkfvObqTWgpzpyV/1OhZYDUlXJqiADYnyAsQL8WV2uFQ5fRG7baVDiNYn4uE92Of
FN7jXmT+7UcHSO5L0VqHS6Ia6j2NammKlbhsAw7dU3uxEgCJvBYlWSY+z9Fs+O1B
hNU42fL5W7JnoGfqG+LqfDdbG0MG+GMo1rDDcvrEK73F41w7mLmZhfYAZY26xU6Q
XyLcrG4YfR6FCyOs53zwrKKG9x3ETqY3S0DSSOKTnuvIGZBCSOxGpqdb7pQax6IJ
PqsM+IzFC1C0Ol0eX1ee734rCHfNP6XEM7ucjBweNUJRrU0Q5CrGGJzEQz0ym/JN
81f/kQJJosSSc1+j8iDiaKkUgcQ1DWWZLYZgvCkwZcY=
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI2MDYy
MjEwNDc0OFoYDzIzOTEwNDA1MTA0NzQ4WjA8MBMCAgIrFw0yNjA3MDExMDQ3NDha
MCUCFAqXdSGI9kR+CnND4uvmc33Wb+59Fw0yNjA3MDExMDQ3NDhaoCMwITAfBgNV
HSMEGDAWgBT+K/WumDBi4hRISudTeDt/ppiG7TANBgkqhkiG9w0BAQ0FAAOCAgEA
fxBxN6giXdLaG5lWLbigI78WQoEzOxWeYbzUpqmn+QFd9YiJCNMaGz2XWl/MBZEl
gmpHNOEzOM5Ze5dRfbLDAzBBFghNGrDDQURMw0ASZfHX6WsL7MOoBqTeg4xbFESk
dXT9YCH/wS5sn5YgkKDh2jqHQya8c4TtWfuWYW/Anyo8wVhgcYAnAiSJZgSUciq5
964Ci2v4pifpQBbpx1MAaOgU7kkY59v+AHhWfxJ7whJra8DA+v6XEim7Y/uABW0N
Yzp+rcSZdDanFq5OC8YE9Kn0c7bHvFi0bDf/AmOB9D64OHc3PRQInubN7aNaQ89N
T+wgFhN2yMSuh4YB+RJ3C1aouGszzC275vYEcc2Iw2OdQQkTu7dhhV+pEfvkhh4x
myBgJKEBqidXDAe1SjGtNP+G0pF6XcQDc3lkSsw+btW1YjQIbv7uayuDfz7aqbh8
vPdP5QKWocPkIOL8sv4JFiETfVWqVMH42vE+ixC3+enunLxqJAUxqMZSfKuCtPYb
PhnSuZZ+rTfXpFfYZMMUTMhrx618+uttcQGosdYNv9e4YSR8pLvIPFef/wfWqpLL
Yy8xYSGOUJ9f8vfh0aIkcRiLbjSgnyJeSlQVRnF0w4FeTjm3QeY2gcdMzaIo3oyH
G23dJvzGq19Y5h4ShZoACuasTIKtaxLm0gjwCYBWZO8=
-----END X509 CRL-----

View File

@@ -1,38 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGsTCCBJmgAwIBAgIUMwTHYM3peBjwVRi6iICr6FVn0cMwDQYJKoZIhvcNAQEL
MIIGuDCCBKCgAwIBAgIUMX4D3d8lY30ZIeB/PpAi4vEPvfcwDQYJKoZIhvcNAQEL
BQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMR4wHAYDVQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUg
Q0EwIBcNMjQwMzIxMTQ1MjM4WhgPMjM4ODEyMjMxNDUyMzhaMIHMMQswCQYDVQQG
Q0EwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3NDhaMIHMMQswCQYDVQQG
EwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBD
b3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNo
aW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlvcmsxFTATBgNVBAcMDFBv
dWdoa2VlcHNpZTEnMCUGA1UECwweSUJNIFogSG9zdCBLZXkgU2lnbmluZyBTZXJ2
aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxNqcFB9GSj+nC1PV
OTIAvzTuo3X7bTbohEPrIDDsEcGyVzQtfagI9vkxhhGLhXxIXrkwEI4u75DeLBdq
ZR3KFJ3QD+jHW12EWNpmSYu272CEAJeb/SBJaFbh40WbY1BsTP+3zI5QMwVTmCH5
QqODD+GU0fDSo9Gti0yX4rxOY+54jDC77AhSlU55rYruBoIXVe4CcPUpqzZLgqB2
cTJ5g7lokGqc6w9pveCznVYGPfcdDt+ePGssOvrfBOJnn6N44tTRAG623BrpctXi
t3IAhG60y6CqF29BjV0RKOv09nOBiph2a3lAiKYrpAt92BETFB7KSQManUrrvWOD
mYEcoVkzvrVrB3WnCcHs6lsjQg1FcNrGcDzV9SmX0BIhM+fasxZyHWc08pr3NrRn
Eks7lRqY+TS8DAeZYJt/2M0Jr32d6Bh+WdyGFFUj6sBPtCaC2VnSkxOgKXeWf/c5
EUXIQT2YXEkNK9CP8Kqs8IerEIpfceelQReA5QcvNruaJNktWLKJgYTrdLdfOP0u
s+9JJHWcFH3tv5906CA+Tlm53Dk6SaRc/DB7lu94yTkcSOpxXZnximZi0GcPZYmZ
TaswE7d8HCPLhks/RkZSV2764Kl08xle65APiWZ4dM24uSyu75Izb4hrwgIA4qyM
dDVQnI3cu6utmKUPyHoJtH26dr0CAwEAAaOBlTCBkjAdBgNVHR8EFjAUMBKgEKAO
hgxpbnRlcl9jYS5jcmwwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYD
VR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU+f7h8O2ttBHtwI4OZnpD9wU0
9GYwHQYDVR0OBBYEFMOMHl201gGYrA/OoGUroGeldRTZMA0GCSqGSIb3DQEBCwUA
A4ICAQBfBZZ7ZPkRjRgk+0/7CGam2AAv/GSuma8lOnJ6IpBEUL0D01reRe9NO1Y5
iXasGZ80e95oC7WtePt1LS2I5rOOb3No0iHcUTm5lZV2M/ObAOwraQLXePCRVgU/
OQzNhtRoCpqgL80/1Ne+MTHj1yZp2GRKXOdMd39KZoG92JU/h556GQ6reN6WKNs+
wbFG+JKiRnfvPsul6J5lG4QuObCAZXa0fhcQNUHyWjCGIhMa8AuhkDDlIEOBV1Vx
T/ixdoFhDIoz0xExFidR5uqYXKZlgzcMZ4JT7x0Vs/YsOIg9z5PNkyjt8Kjrxvqs
NZ1eYuNNxdXxdJZ2x04y8AzLkLRYntOCks5nEamQtzv0ice1Jg52qNJnMm0Y0U9o
7BjlWy/pp8dBfIA+FyxsIs62hAE6Z/vuJa1VuWJblrT1MFM48Bh64p1m5uDXAZ9w
Oy7n6YDpzWKWqn6XNV6FoDSkkwaOwB6bPaqAP1ZvP3BmVhednyYRYY21M2aOmxos
7rpaW1FHgYV96Dm60rL7XRtOEYZNPSIPDO/Ro1oWs3EFwjLLAWtlK1HdMswlnVDX
6M+LNCRZA3foNi6xvf7CvPkshtQe3FOfiw/OiPBsYsYREC+90Ml/oeTIsyCaOF6n
5+dpjCBeOUaxzfeQI4WCrLG6+iQZNXWgBkW/PHaPZTldRWzvdQ==
aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArwM7pHOyjneIEu8d
n/W8nHZYL3LOLSP4K7LDa3s2bxIOa4oFh/C0Q8RIBslyIq0bK6ggVmXBHiKirXlo
jQvACF9hqOCx0kFwXNF4CGFQ+gA/z0bs17LJbENPa9nWqpoQ6FbDlMM9RyevAQtu
FKPPO6KlCKBCVBGbfzzqBZNogfmq3hsjEtL+2jUpIT4WfUiDi6Azjx0hGhmAQ97N
01KyjcZd4GuevsxFq9seuogYkEG7TmLgRGpNKRT8NhZFi+mKJP/bXvYZEb/SaGUo
gHiA3O3R0S8EQEnVsfB1ASMdpVoI0wV6XO8JKEguZZBUF1roZEPmq6CChpNpdvGu
oOTCgsNhAsVRjz70TTZQii0ktemREOqUmFMXSNeKwPlEz5jNSHLiuJksk+dIw33Y
LBEMTxx0ckqZKJGK/DbsjKfoeMdioRl1fMy0B2a3TYprp5linnYmoXSl2B0gbdlE
A37LgEkTtBVOw++fxlDZN8FsB6VyP161CwBA0LjGuGHcghNvEQi0QS+dFbc/9j9A
tRr92+cvxIezi2F6s1dcKxjTuAzdXeEliw2C37NE87zG8s5uX4VeVOuCZQ3VmueU
my5zplN1oJDFNQWfjayEOurnde0T73tFTnpagYhpXwxOKTSUSzEsmh/57TlasY0L
zlh6k4a4jvfj26Ve+y+NMAqrznkCAwEAAaOBnDCBmTAkBgNVHR8EHTAbMBmgF6AV
hhNodHRwOi8vaW50ZXJfY2EuY3JsMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD
AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFHvgduY20uw75WX7
Feb/jSCXuAmoMB0GA1UdDgQWBBT+K/WumDBi4hRISudTeDt/ppiG7TANBgkqhkiG
9w0BAQsFAAOCAgEANPj1emmO06HnGnZCRnsTldd1f+ghOtIZxTxKB3dSBVJNafv/
jNh4rRvKyQ6osY2uG/tydxOhHxr7XMP1Fca98iJvrdM4Ox5XwtoltesBzB16zZFJ
/ekzAF+ttISRlMrOlS6q+Yph+cSBnG2VMlPiEfyvW6fkT+OQIwPT5t4wvYB1eN4T
9/6iSDaV0P8n/7S2itIXjl3TzhhRN6RFeC0TGfbPasCHKKkA/l9wwm6Hijfb3SXG
ke+r+lTz/A6S/3tstuYmZiXyxHsfsI07C77uy23rReF+imyR2Tl7et/+Xaw3Lvp9
b8WZvGWHlXmQlQVLfZzKcvOB4Xnd0wzUENVGiqTGgPxTPAWR+Zwf16iUzkEemUSp
/NERtCKuWImGaycoyPWUXGVDz8novJLHjYv+BLJMskrCunUpxXjbXdDOTZN1VSo1
IPxlQU3oCe29N98N6P5/HjjhJa8i7iQr8xTSGpz58IzKztuBLSXIcKCS9nTV3EOV
LTpK40TjrIGSOrgUFc6VumkufTx5Lm4ZYMcWi3qJJoDvtv5tCoBV5a5l+v9kBAom
AqTBRH6QpBvIEjeEghAntiAE8CI8K+T4W9O8LHVm4LkuK5AAYenx8axnN0BDhLg6
tglhS202ez9SLjRN6Hb8uw5CS61O56gzwPvEf1vyQAhZa1KPIof++GzjHAk=
-----END CERTIFICATE-----

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