Files
s390-tools/rust/pv/src/req/ec_coord.rs
Marc Hartmayer f04e46baf0 pv: req: Improve documentation and fix typos
Improve documentation and fix typos.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00

155 lines
5.2 KiB
Rust

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