@mupuf I wrote the code so that i don’t end up dust-for-linux :D
I also learned that “macros_rules!” is the thing that you would want to learn first in Rust so that it would make any sense if you knew beforehand that it is the thing that you most want to learn :-) Without it super unproductive language and IMHO does not really pay the cost despite what ever “memory safety”. With “macro_rules!” you can consolidate those few dozen trait implementations into one single place :-) Then it is suddenly more productive language than C.
I drive the spec interpration and building with ~600 lines of trial and error fine-tune syntax tree macros :-) https://github.com/puavo-org/tpm2_library/blob/main/protocol/src/macro.rs
And then I can implement the spec like I was writing the spec:
tpm_struct!(
#[derive(Debug, Default, PartialEq, Eq, Clone)]
TpmStartAuthSessionCommand,
TpmCc::StartAuthSession,
true,
true,
2,
{
pub nonce_caller: Tpm2b,
pub encrypted_salt: Tpm2b,
pub session_type: TpmSe,
pub symmetric: TpmtSymDefObject,
pub auth_hash: TpmAlgId,
}
);
And all the bidirectional marshalling and unmarshallinf magic will appear :-) Things like “async” are more like fixed structure threading framework type of stuff but does not convince at all what makes sense in the core language (neither does borrow checker at least not as “driving feature”), I mean we already have Java :-) ).
This makes me happy:
tpm_struct!(
#[derive(Debug, PartialEq, Eq, Clone)]
TpmPcrEventCommand,
TpmCc::PcrEvent,
TpmSt::Sessions,
1,
{
pub event_data: Tpm2b,
}
);
tpm_response!(
#[derive(Debug, Default, PartialEq, Eq, Clone)]
TpmPcrEventResponse,
TpmCc::PcrEvent,
TpmSt::Sessions,
{
pub digests: TpmlDigestValues,
}
);
Also the types inside use the same system (in fact tpm_struct is shared macro with data types and commands). This will generate full parsing and building for both commands and responses - all without heap involved.
It also optimizes performance as that zeros out marshalling and unmarshalling. Internal representation can be buffer to which data structures are mapped.
For capacity it’s not exact science i.e.:
/// Buffer properties for an object.
pub trait TpmBuffer {
/// Capacity at most required to strore an object implementing the type.
const BUFFER_CAPACITY: usize;
pub fn len() -> usize;
}
E.g., for digest this would the maximum digest. For TPMT_PUBLIC capacity would 640 bytes to fit 4096-bit RSA key.
At runtime, however, the exact size is calculated by summing up “recursively”. Lengths also when summed up point the exact location in the byte stream where an attribute is located. I.e., it’s a zerocopy. I