pvimg: info: Improve JSON output

Improve JSON output and implement deserialization.

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>
This commit is contained in:
Marc Hartmayer
2026-02-09 16:26:47 +01:00
committed by Jan Höppner
parent 012025595f
commit 87966251c4
7 changed files with 449 additions and 74 deletions

View File

@@ -10,6 +10,7 @@ workspace = true
[dependencies]
anyhow = { version = "1.0.95", features = ["std"] }
base64 = "0.22.1"
clap = { version ="4.5", features = ["derive", "wrap_help"]}
deku = "0.18"
deku_derive = "0.18"

View File

@@ -31,10 +31,16 @@ pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
let key =
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
let decrypted_hdr = hdr.decrypt(&key)?;
SeH::Decrypted(decrypted_hdr)
SeH::DecryptedSeHdr {
se_hdr: decrypted_hdr,
verified: true,
}
} else {
warn!("WARNING: The Secure Execution header integrity and authenticity was not verified. Specify '--hdr-key' to authenticate it. Do not trust the data without verification.");
SeH::Encrypted(hdr)
SeH::SeHdr {
se_hdr: hdr,
verified: false,
}
};
match opt.format {

View File

@@ -4,9 +4,9 @@
use deku::{ctx::Endian, DekuRead, DekuWrite};
use pv::request::Zeroize;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use super::serializing::ser_lower_hex;
use super::serializing::serde_hex_left_padded_u64;
use crate::pv_utils::error::Error;
pub const PSW32_ADDR_MASK: u64 = 0x000000007fffffff;
@@ -14,12 +14,12 @@ pub const PSW_MASK_BA: u64 = 0x0000000080000000;
pub const PSW_MASK_EA: u64 = 0x0000000100000000;
pub const PSW_MASK_BIT_12: u64 = 0x08000000000000;
#[derive(Default, Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Default, Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct PSW {
#[serde(serialize_with = "ser_lower_hex")]
#[serde(with = "serde_hex_left_padded_u64", rename = "mask_hex")]
pub mask: u64,
#[serde(serialize_with = "ser_lower_hex")]
#[serde(with = "serde_hex_left_padded_u64", rename = "addr_hex")]
pub addr: u64,
}
@@ -30,7 +30,7 @@ impl Zeroize for PSW {
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Default, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct ShortPsw(u64);
@@ -135,4 +135,19 @@ mod tests {
let new_psw: PSW = short_psw.into();
assert_eq!(new_psw, psw);
}
#[test]
fn psw_json_roundtrip() {
let psw = PSW {
addr: 0x1234567890abcdef,
mask: 0xfedcba0987654321,
};
let json = serde_json::to_string(&psw).expect("should serialize");
assert_eq!(
json,
"{\"mask_hex\":\"fedcba0987654321\",\"addr_hex\":\"1234567890abcdef\"}"
);
let deserialized: PSW = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(psw, deserialized);
}
}

View File

@@ -15,7 +15,7 @@ use pv::{
},
static_assert,
};
use serde::Serialize;
use serde::{Deserialize, Serialize};
pub use super::hdr_v1::{SeHdrBinV1, SeHdrDataV1};
use super::{PlaintextControlFlagsV1, SecretControlFlagsV1};
@@ -23,7 +23,7 @@ use crate::{
misc::PAGESIZE,
pv_utils::{
error::{Error, Result},
serializing::{ser_hex, serialize_to_bytes},
serializing::{serde_hex_array, serialize_to_bytes},
uvdata::{
AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait,
UvDataTrait,
@@ -35,7 +35,7 @@ use crate::{
#[repr(u32)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(
endian = "endian",
id_type = "u32",
@@ -47,26 +47,37 @@ pub enum SeHdrVersion {
V1 = 0x100,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
#[serde(rename_all = "snake_case")]
pub enum SeH {
Decrypted(SeHdrPlain),
Encrypted(SeHdr),
DecryptedSeHdr {
verified: bool,
#[serde(flatten)]
se_hdr: SeHdrPlain,
},
SeHdr {
verified: bool,
#[serde(flatten)]
se_hdr: SeHdr,
},
}
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct SeHdrCommon {
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "magic_hex")]
pub magic: [u8; 8],
pub version: SeHdrVersion,
}
static_assert!(::std::mem::size_of::<SeHdrCommon>() == 12);
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct SeHdrCommonWithSize {
#[serde(with = "serde_hex_array", rename = "magic_hex")]
pub magic: [u8; 8],
pub version: SeHdrVersion,
pub sehs: u32,
@@ -85,7 +96,7 @@ impl SeHdrCommon {
}
}
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "Endian::Big")]
/// Secure Execution header structure
pub struct SeHdr {
@@ -97,7 +108,7 @@ pub struct SeHdr {
pub data: SeHdrVersioned,
}
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "Endian::Big")]
/// Plain data Secure Execution header structure
pub struct SeHdrPlain {
@@ -109,7 +120,7 @@ pub struct SeHdrPlain {
}
#[enum_dispatch(AeadCipherTrait, AeadDataTrait, KeyExchangeTrait)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
#[serde(untagged)]
#[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")]
pub enum SeHdrVersioned {
@@ -123,7 +134,7 @@ pub enum SeHdrVersioned {
KeyExchangeTrait,
KeyExchangeBuilderTrait
)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize, Deserialize)]
#[serde(untagged)]
#[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")]
pub enum SeHdrData {
@@ -382,7 +393,7 @@ impl AeadCipherTrait for SeHdrPlain {
mod tests {
use std::io::Cursor;
use super::SeHdr;
use super::*;
use crate::error::Error;
#[test]

View File

@@ -14,7 +14,7 @@ use pv::request::{
random_array, Aes256XtsKey, Confidential, EcPubKeyCoord, Encrypt, Keyslot, SymKey, SymKeyType,
Zeroize, SHA_512_HASH_LEN,
};
use serde::{Serialize, Serializer};
use serde::{Deserialize, Serialize};
use super::keys::phkh_v1;
use crate::{
@@ -31,7 +31,8 @@ use crate::{
},
serializing::{
bytesize, bytesize_confidential, confidential_read_slice, confidential_write_slice,
ser_hex, ser_hex_confidential, ser_lower_hex, serialize_to_bytes,
serde_base64, serde_hex_array, serde_hex_confidential_array, serde_hex_left_padded_u64,
serialize_to_bytes,
},
try_copy_slice_to_array,
uvdata::{
@@ -49,27 +50,28 @@ struct HdrSizesV1 {
pub sea: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct SeHdrAadV1 {
#[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
pub sehs: u32,
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "iv_hex")]
pub iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
#[serde(skip)]
res1: u32,
#[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
pub nks: u64,
#[deku(assert = "*sea <= (*sehs).into()")]
pub sea: u64,
pub nep: u64,
#[serde(serialize_with = "ser_lower_hex")]
#[serde(with = "serde_hex_left_padded_u64", rename = "pcf_hex")]
pub pcf: u64,
pub cust_pub_key: EcPubKeyCoordV1,
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "pld_hex")]
pub pld: [u8; SHA_512_HASH_LEN],
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "ald_hex")]
pub ald: [u8; SHA_512_HASH_LEN],
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "tld_hex")]
pub tld: [u8; SHA_512_HASH_LEN],
#[deku(count = "nks")]
pub keyslots: Vec<BinaryKeySlotV1>,
@@ -104,27 +106,30 @@ impl KeyExchangeTrait for SeHdrAadV1 {
}
}
#[derive(PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)]
#[derive(PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct SeHdrConfV1 {
#[serde(serialize_with = "ser_hex_confidential")]
#[serde(with = "serde_hex_confidential_array", rename = "cck_hex")]
#[deku(
reader = "confidential_read_slice(deku::reader, endian)",
writer = "confidential_write_slice(cck, deku::writer, endian)"
)]
cck: Confidential<[u8; 32]>,
#[serde(serialize_with = "ser_hex_confidential")]
#[serde(with = "serde_hex_confidential_array", rename = "xts_hex")]
#[deku(
reader = "confidential_read_slice(deku::reader, endian)",
writer = "confidential_write_slice(xts, deku::writer, endian)"
)]
xts: Aes256XtsKey,
psw: PSW,
#[serde(serialize_with = "ser_lower_hex")]
#[serde(with = "serde_hex_left_padded_u64", rename = "scf_hex")]
pub scf: u64,
#[serde(skip)]
#[deku(assert_eq = "0")]
noi: u32,
#[serde(skip)]
res2: u32,
#[serde(skip)]
#[deku(count = "noi")]
opt_items: Vec<u8>,
}
@@ -141,27 +146,41 @@ impl Zeroize for SeHdrConfV1 {
}
}
#[derive(Default, PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)]
#[derive(Default, PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct SeHdrTagV1 {
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "tag_hex")]
tag: [u8; SymKeyType::AES_256_GCM_TAG_LEN],
}
fn ser_confidential_confv1<S: Serializer>(
encrypted: &Confidential<SeHdrConfV1>,
ser: S,
) -> std::result::Result<S::Ok, S::Error> {
encrypted.value().serialize(ser)
mod ser_confidential_confv1 {
use pv::request::Confidential;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::SeHdrConfV1;
pub fn serialize<S: Serializer>(
encrypted: &Confidential<SeHdrConfV1>,
ser: S,
) -> Result<S::Ok, S::Error> {
encrypted.value().serialize(ser)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Confidential<SeHdrConfV1>, D::Error> {
let conf = SeHdrConfV1::deserialize(deserializer)?;
Ok(Confidential::new(conf))
}
}
/// Secure Execution Header definition
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "big")]
pub struct SeHdrDataV1 {
#[serde(flatten)]
pub aad: SeHdrAadV1,
#[serde(flatten, serialize_with = "ser_confidential_confv1")]
#[serde(flatten, with = "ser_confidential_confv1")]
#[deku(
reader = "confidential_read_sehdrconf_v1(deku::reader)",
writer = "confidential_write_sehdrconf_v1(data, deku::writer)"
@@ -439,14 +458,14 @@ impl SeHdrPubBuilderTrait for SeHdrDataV1 {
}
}
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "big")]
pub struct SeHdrBinV1 {
#[serde(flatten)]
pub aad: SeHdrAadV1,
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_base64", rename = "cipher_data_b64")]
#[deku(bytes_read = "aad.sea")]
pub data: Vec<u8>,
pub cipher_data: Vec<u8>,
#[serde(flatten)]
pub tag: SeHdrTagV1,
}
@@ -512,7 +531,7 @@ impl AeadDataTrait for SeHdrBinV1 {
}
fn data(&self) -> Vec<u8> {
self.data.to_owned()
self.cipher_data.to_owned()
}
fn tag(&self) -> Vec<u8> {
@@ -667,4 +686,93 @@ mod tests {
.expect("should not fail");
assert!(matches!(builder.build(), Err(Error::InvalidSeHdr)));
}
#[test]
fn roundtrip_se_hdr_tag_v1_json() {
let tag = SeHdrTagV1 {
tag: [0x42; SymKeyType::AES_256_GCM_TAG_LEN],
};
let json = serde_json::to_string(&tag).expect("should serialize");
assert_eq!(json, "{\"tag_hex\":\"42424242424242424242424242424242\"}");
let deserialized: SeHdrTagV1 = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(tag, deserialized);
}
#[test]
fn roundtrip_se_hdr_conf_v1_json() {
let conf = SeHdrConfV1 {
cck: Confidential::new([0x11; 32]),
xts: Confidential::new([0x22; SymKeyType::AES_256_XTS_KEY_LEN]),
psw: PSW {
addr: 0x1000,
mask: 0x2000,
},
scf: 0x42,
noi: 0,
res2: 0,
opt_items: vec![],
};
let json = serde_json::to_string(&conf).expect("should serialize");
assert_eq!(json, "{\"cck_hex\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"xts_hex\":\"22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222\",\"psw\":{\"mask_hex\":\"0000000000002000\",\"addr_hex\":\"0000000000001000\"},\"scf_hex\":\"0000000000000042\"}");
let deserialized: SeHdrConfV1 = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(conf, deserialized);
}
#[test]
fn roundtrip_se_hdr_aad_v1_json() {
let aad = SeHdrAadV1 {
sehs: 1024,
iv: [0x33; SymKeyType::AES_256_GCM_IV_LEN],
res1: 0,
nks: 2,
sea: 512,
nep: 10,
pcf: 0x100,
cust_pub_key: EcPubKeyCoordV1 { coord: [0x44; 160] },
pld: [0x55; SHA_512_HASH_LEN],
ald: [0x66; SHA_512_HASH_LEN],
tld: [0x77; SHA_512_HASH_LEN],
keyslots: vec![],
};
let json = serde_json::to_string(&aad).expect("should serialize");
assert_eq!(json, "{\"sehs\":1024,\"iv_hex\":\"333333333333333333333333\",\"nks\":2,\"sea\":512,\"nep\":10,\"pcf_hex\":\"0000000000000100\",\"cust_pub_key\":{\"coord_hex\":\"44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444\"},\"pld_hex\":\"55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555\",\"ald_hex\":\"66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666\",\"tld_hex\":\"77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777\",\"keyslots\":[]}");
let deserialized: SeHdrAadV1 = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(aad, deserialized);
}
#[test]
fn roundtrip_se_hdr_bin_v1_json() {
let bin = SeHdrBinV1 {
aad: SeHdrAadV1 {
sehs: 1024,
iv: [0x33; SymKeyType::AES_256_GCM_IV_LEN],
res1: 0,
nks: 0,
sea: 64,
nep: 10,
pcf: 0x100,
cust_pub_key: EcPubKeyCoordV1 { coord: [0x44; 160] },
pld: [0x55; SHA_512_HASH_LEN],
ald: [0x66; SHA_512_HASH_LEN],
tld: [0x77; SHA_512_HASH_LEN],
keyslots: vec![],
},
cipher_data: vec![0x88; 64],
tag: SeHdrTagV1 {
tag: [0x99; SymKeyType::AES_256_GCM_TAG_LEN],
},
};
let json = serde_json::to_string(&bin).expect("should serialize");
assert_eq!(json, "{\"sehs\":1024,\"iv_hex\":\"333333333333333333333333\",\"nks\":0,\"sea\":64,\"nep\":10,\"pcf_hex\":\"0000000000000100\",\"cust_pub_key\":{\"coord_hex\":\"44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444\"},\"pld_hex\":\"55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555\",\"ald_hex\":\"66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666\",\"tld_hex\":\"77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777\",\"keyslots\":[],\"cipher_data_b64\":\"iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiA==\",\"tag_hex\":\"99999999999999999999999999999999\"}");
let deserialized: SeHdrBinV1 = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(bin, deserialized);
}
}

View File

@@ -10,11 +10,11 @@ use openssl::{
pkey::{PKey, PKeyRef, Public},
};
use pv::{request::EcPubKeyCoord, static_assert};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use crate::{
error::{Error, Result},
pv_utils::{serializing::ser_hex, try_copy_slice_to_array},
pv_utils::{serializing::serde_hex_array, try_copy_slice_to_array},
};
/// Try to hash the public EC key.
@@ -28,10 +28,10 @@ pub fn phkh_v1<T: AsRef<PKeyRef<Public>>>(key: T) -> Result<[u8; 32]> {
try_copy_slice_to_array(&binding)
}
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
pub struct EcPubKeyCoordV1 {
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "coord_hex")]
pub coord: [u8; 160],
}
@@ -68,18 +68,18 @@ impl TryFrom<EcPubKeyCoordV1> for PKey<Public> {
}
#[repr(C)]
#[derive(Default, Debug, PartialEq, Eq, Clone, DekuRead, DekuWrite, Serialize)]
#[derive(Default, Debug, PartialEq, Eq, Clone, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
/// Binary key slot v1
pub struct BinaryKeySlotV1 {
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "phkh_hex")]
/// Public host key hash
pub phkh: [u8; 32],
/// Wrapper key
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "wrpk_hex")]
pub wrpk: [u8; 32],
/// Tag
#[serde(serialize_with = "ser_hex")]
#[serde(with = "serde_hex_array", rename = "kst_hex")]
pub kst: [u8; 16],
}
static_assert!(size_of::<BinaryKeySlotV1>() == 80);
@@ -97,3 +97,44 @@ impl TryFrom<Vec<u8>> for BinaryKeySlotV1 {
Ok(bin)
}
}
#[cfg(test)]
mod serde_tests {
use super::*;
#[test]
fn roundtrip_ecpubkey_json() {
let key = EcPubKeyCoordV1 { coord: [0x42; 160] };
let json = serde_json::to_string(&key).expect("should serialize");
assert_eq!(json,
"{\"coord_hex\":\"42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242\"}");
let deserialized: EcPubKeyCoordV1 =
serde_json::from_str(&json).expect("should deserialize");
assert_eq!(key, deserialized);
}
#[test]
fn roundtrip_keyslots_json() {
let keyslots = vec![
BinaryKeySlotV1 {
phkh: [0x01; 32],
wrpk: [0x02; 32],
kst: [0x03; 16],
},
BinaryKeySlotV1 {
phkh: [0x03; 32],
wrpk: [0x04; 32],
kst: [0x05; 16],
},
];
let json = serde_json::to_string(&keyslots).expect("should serialize");
assert_eq!(json, "[{\"phkh_hex\":\"0101010101010101010101010101010101010101010101010101010101010101\",\"wrpk_hex\":\"0202020202020202020202020202020202020202020202020202020202020202\",\"kst_hex\":\"03030303030303030303030303030303\"},{\"phkh_hex\":\"0303030303030303030303030303030303030303030303030303030303030303\",\"wrpk_hex\":\"0404040404040404040404040404040404040404040404040404040404040404\",\"kst_hex\":\"05050505050505050505050505050505\"}]");
let deserialized: Vec<BinaryKeySlotV1> =
serde_json::from_str(&json).expect("should deserialize");
assert_eq!(keyslots, deserialized);
}
}

View File

@@ -2,37 +2,132 @@
//
// Copyright IBM Corp. 2024
use std::fmt::LowerHex;
use deku::{
reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite, DekuError, DekuReader,
DekuWriter,
};
use pv::request::{Confidential, Zeroize};
use serde::{Serialize, Serializer};
use utils::HexSlice;
use crate::pv_utils::error::Result;
pub fn ser_hex<A: AsRef<[u8]>, S: Serializer>(
data: A,
ser: S,
) -> std::result::Result<S::Ok, S::Error> {
HexSlice::from(data.as_ref()).serialize(ser)
pub mod serde_hex_left_padded_u64 {
use std::fmt::LowerHex;
use serde::Deserializer;
use serde::{Deserialize, Serialize, Serializer};
pub fn serialize<S: Serializer, B: LowerHex>(
data: &B,
serializer: S,
) -> Result<S::Ok, S::Error> {
format!("{data:016x}").serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
let s = String::deserialize(deserializer)?;
u64::from_str_radix(&s, 16).map_err(serde::de::Error::custom)
}
}
pub fn ser_lower_hex<S: Serializer, B: LowerHex>(
data: &B,
ser: S,
) -> std::result::Result<S::Ok, S::Error> {
format!("{data:#018x}").serialize(ser)
pub mod serde_hex_array {
use std::result::Result;
use pv::misc::{decode_hex, encode_hex};
use serde::{Deserialize, Deserializer, Serializer};
use crate::pv_utils::try_copy_slice_to_array;
pub fn serialize<S: Serializer, const COUNT: usize>(
data: &[u8; COUNT],
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&encode_hex(data))
}
#[allow(dead_code)]
pub fn deserialize<'de, D: Deserializer<'de>, const COUNT: usize>(
deserializer: D,
) -> Result<[u8; COUNT], D::Error> {
let s = String::deserialize(deserializer)?;
let decoded = decode_hex(s).map_err(serde::de::Error::custom)?;
try_copy_slice_to_array(&decoded).map_err(serde::de::Error::custom)
}
}
pub fn ser_hex_confidential<S: Serializer, const COUNT: usize>(
data: &Confidential<[u8; COUNT]>,
ser: S,
) -> std::result::Result<S::Ok, S::Error> {
ser_hex(data.value(), ser)
pub mod serde_hex_confidential_array {
use std::result::Result;
use pv::request::Confidential;
use serde::{Deserializer, Serializer};
use super::serde_hex_array;
pub fn serialize<S: Serializer, const COUNT: usize>(
data: &Confidential<[u8; COUNT]>,
serializer: S,
) -> Result<S::Ok, S::Error> {
serde_hex_array::serialize(data.value(), serializer)
}
#[allow(dead_code)]
pub fn deserialize<'de, D: Deserializer<'de>, const COUNT: usize>(
deserializer: D,
) -> Result<Confidential<[u8; COUNT]>, D::Error> {
Ok(Confidential::new(serde_hex_array::deserialize(
deserializer,
)?))
}
}
pub mod serde_base64 {
use base64::prelude::*;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S, B>(bytes: B, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
B: AsRef<[u8]>,
{
let b64 = BASE64_STANDARD.encode(bytes.as_ref());
serializer.serialize_str(&b64)
}
#[allow(dead_code)]
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
BASE64_STANDARD.decode(&s).map_err(serde::de::Error::custom)
}
}
pub mod serde_base64_array {
use base64::prelude::*;
use serde::{Deserialize, Deserializer, Serializer};
use crate::pv_utils::try_copy_slice_to_array;
#[allow(dead_code)]
pub fn serialize<S, const N: usize>(bytes: &[u8; N], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let b64 = BASE64_STANDARD.encode(bytes);
serializer.serialize_str(&b64)
}
#[allow(dead_code)]
pub fn deserialize<'de, D, const COUNT: usize>(deserializer: D) -> Result<[u8; COUNT], D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let decoded = BASE64_STANDARD
.decode(&s)
.map_err(serde::de::Error::custom)?;
try_copy_slice_to_array(&decoded).map_err(serde::de::Error::custom)
}
}
/// Read a slice into a confidential array of type [`T`] and length [`N`].
@@ -146,4 +241,102 @@ mod tests {
assert_eq!(test.test.value()[0], 0x0f010203);
assert_eq!(test.to_bytes().unwrap().as_slice(), &DATA);
}
#[test]
fn test_serde_base64() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestStruct {
#[serde(with = "super::serde_base64")]
data: Vec<u8>,
}
let original = TestStruct {
data: vec![1, 2, 3, 4, 5],
};
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains("AQIDBAU="));
let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_serde_base64_with_array() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestStruct {
#[serde(with = "super::serde_base64_array")]
data: [u8; 5],
}
let original = TestStruct {
data: [1, 2, 3, 4, 5],
};
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains("AQIDBAU="));
let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_serde_hex_array() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestStruct {
#[serde(with = "super::serde_hex_array")]
data: [u8; 5],
}
let original = TestStruct {
data: [1, 2, 3, 4, 5],
};
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains("0102030405"));
assert_eq!(&json, r#"{"data":"0102030405"}"#);
let deserialized: TestStruct = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn test_serde_hex_array_invalid_length() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestStruct {
#[serde(with = "super::serde_hex_array")]
data: [u8; 5],
}
// Try to deserialize with wrong length (3 bytes instead of 5)
let json = r#"{"data":"010203"}"#;
let result: Result<TestStruct, _> = serde_json::from_str(json);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Expected size 5"));
}
#[test]
fn test_serde_base64_array_invalid_length() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestStruct {
#[serde(with = "super::serde_base64_array")]
data: [u8; 5],
}
// Try to deserialize with wrong length (3 bytes instead of 5)
let json = r#"{"data":"AQID"}"#; // Base64 for [1, 2, 3]
let result: Result<TestStruct, _> = serde_json::from_str(json);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Expected size 5"));
}
}