Files
khadhroony-solana-project/crates/ksp-wallet-lib/src/transcript.rs

131 lines
5.9 KiB
Rust

// file: crates/ksp-wallet-lib/src/transcript.rs
// version: 2
//! Deterministic `.kspwallet` V1 state-transcript and AEAD-AAD encoding.
const TAG_COMPARTMENT_ALGORITHM: u16 = 0x0202;
const TAG_COMPARTMENT_CIPHERTEXT: u16 = 0x0204;
const TAG_COMPARTMENT_KIND: u16 = 0x0200;
const TAG_COMPARTMENT_NONCE: u16 = 0x0203;
const TAG_COMPARTMENT_VERSION: u16 = 0x0201;
const TAG_FORMAT_VERSION: u16 = 0x0002;
const TAG_KDF_ALGORITHM: u16 = 0x0102;
const TAG_KDF_ITERATIONS: u16 = 0x0105;
const TAG_KDF_MEMORY_KIB: u16 = 0x0104;
const TAG_KDF_PARALLELISM: u16 = 0x0106;
const TAG_KDF_SALT: u16 = 0x0107;
const TAG_KDF_VERSION: u16 = 0x0103;
const TAG_MAGIC: u16 = 0x0001;
const TAG_OWNER_AUTH_PUBLIC_KEY: u16 = 0x0003;
const TAG_SLOT_ID: u16 = 0x0100;
const TAG_SLOT_ROLE: u16 = 0x0101;
const TAG_STATE_SIGNATURE_ALGORITHM: u16 = 0x0500;
const TAG_VIEW_ENABLED: u16 = 0x0010;
const TAG_VIEW_ROLE: u16 = 0x0011;
const TAG_VIEW_SLOT_ID: u16 = 0x0012;
const TAG_WRAP_ALGORITHM: u16 = 0x0108;
const TAG_WRAP_CIPHERTEXT: u16 = 0x010A;
const TAG_WRAP_NONCE: u16 = 0x0109;
/// Builds the normative OWNER state-signature transcript for one validated V1 envelope.
pub(crate) fn state_transcript(envelope: &crate::KspWalletEnvelopeV1) -> std::vec::Vec<u8> {
let mut output = start(crate::KSPWALLET_V1_STATE_TRANSCRIPT_DOMAIN);
push_common(&mut output, envelope);
push_bool(&mut output, TAG_VIEW_ENABLED, envelope.view_descriptor().enabled());
push_bytes(&mut output, TAG_VIEW_ROLE, crate::WalletKeySlotRoleV1::View.as_str().as_bytes());
match envelope.view_descriptor().slot_id() {
std::option::Option::Some(slot_id) => push_bytes(&mut output, TAG_VIEW_SLOT_ID, slot_id),
std::option::Option::None => push_bytes(&mut output, TAG_VIEW_SLOT_ID, &[]),
}
push_slot(&mut output, envelope.owner_slot(), true);
push_compartment(&mut output, envelope.owner_control(), true);
push_compartment(&mut output, envelope.metadata(), true);
push_compartment(&mut output, envelope.secret(), true);
push_bytes(&mut output, TAG_STATE_SIGNATURE_ALGORITHM, envelope.state_signature().algorithm().as_str().as_bytes());
return output;
}
/// Builds the normative wrapping AAD for one validated OWNER or VIEW key slot.
pub(crate) fn slot_aad(envelope: &crate::KspWalletEnvelopeV1, slot: &crate::WalletKeySlotV1) -> std::vec::Vec<u8> {
let domain = match slot.role() {
crate::WalletKeySlotRoleV1::Owner => crate::KSPWALLET_V1_OWNER_SLOT_AAD_DOMAIN,
crate::WalletKeySlotRoleV1::View => crate::KSPWALLET_V1_VIEW_SLOT_AAD_DOMAIN,
};
let mut output = start(domain);
push_common(&mut output, envelope);
push_slot(&mut output, slot, false);
return output;
}
/// Builds the normative AEAD AAD for one validated encrypted compartment.
pub(crate) fn compartment_aad(envelope: &crate::KspWalletEnvelopeV1, compartment: &crate::WalletEncryptedCompartmentV1) -> std::vec::Vec<u8> {
let domain = match compartment.kind() {
crate::WalletCompartmentKindV1::OwnerControl => crate::KSPWALLET_V1_OWNER_CONTROL_AAD_DOMAIN,
crate::WalletCompartmentKindV1::Metadata => crate::KSPWALLET_V1_METADATA_AAD_DOMAIN,
crate::WalletCompartmentKindV1::Secret => crate::KSPWALLET_V1_SECRET_AAD_DOMAIN,
};
let mut output = start(domain);
push_common(&mut output, envelope);
push_compartment(&mut output, compartment, false);
return output;
}
fn start(domain: &[u8]) -> std::vec::Vec<u8> {
let mut output = std::vec::Vec::with_capacity(512);
output.extend_from_slice(domain);
output.push(0);
return output;
}
fn push_common(output: &mut std::vec::Vec<u8>, envelope: &crate::KspWalletEnvelopeV1) {
push_bytes(output, TAG_MAGIC, crate::KSPWALLET_MAGIC.as_bytes());
push_u32(output, TAG_FORMAT_VERSION, envelope.format_version());
push_bytes(output, TAG_OWNER_AUTH_PUBLIC_KEY, envelope.owner_auth_public_key());
}
fn push_slot(output: &mut std::vec::Vec<u8>, slot: &crate::WalletKeySlotV1, include_wrap_payload: bool) {
push_bytes(output, TAG_SLOT_ID, slot.slot_id());
push_bytes(output, TAG_SLOT_ROLE, slot.role().as_str().as_bytes());
push_bytes(output, TAG_KDF_ALGORITHM, slot.kdf().algorithm().as_str().as_bytes());
push_u32(output, TAG_KDF_VERSION, slot.kdf().version());
push_u32(output, TAG_KDF_MEMORY_KIB, slot.kdf().memory_kib());
push_u32(output, TAG_KDF_ITERATIONS, slot.kdf().iterations());
push_u32(output, TAG_KDF_PARALLELISM, slot.kdf().parallelism());
push_bytes(output, TAG_KDF_SALT, slot.kdf().salt());
push_bytes(output, TAG_WRAP_ALGORITHM, slot.wrap().algorithm().as_str().as_bytes());
if include_wrap_payload {
push_bytes(output, TAG_WRAP_NONCE, slot.wrap().nonce());
push_bytes(output, TAG_WRAP_CIPHERTEXT, slot.wrap().ciphertext());
}
}
fn push_compartment(output: &mut std::vec::Vec<u8>, compartment: &crate::WalletEncryptedCompartmentV1, include_ciphertext: bool) {
push_bytes(output, TAG_COMPARTMENT_KIND, compartment.kind().as_str().as_bytes());
push_u32(output, TAG_COMPARTMENT_VERSION, compartment.payload_version());
push_bytes(output, TAG_COMPARTMENT_ALGORITHM, compartment.algorithm().as_str().as_bytes());
if include_ciphertext {
push_bytes(output, TAG_COMPARTMENT_NONCE, compartment.nonce());
push_bytes(output, TAG_COMPARTMENT_CIPHERTEXT, compartment.ciphertext());
}
}
fn push_bool(output: &mut std::vec::Vec<u8>, tag: u16, value: bool) {
let byte = if value { 1_u8 } else { 0_u8 };
push_bytes(output, tag, &[byte]);
}
fn push_u32(output: &mut std::vec::Vec<u8>, tag: u16, value: u32) {
push_bytes(output, tag, value.to_be_bytes().as_slice());
}
fn push_bytes(output: &mut std::vec::Vec<u8>, tag: u16, value: &[u8]) {
output.extend_from_slice(tag.to_be_bytes().as_slice());
let length = value.len() as u64;
output.extend_from_slice(length.to_be_bytes().as_slice());
output.extend_from_slice(value);
}
#[cfg(test)]
#[path = "../unit_tests/transcript.rs"]
mod tests;