v0.2.5-pre.003
This commit is contained in:
71
crates/ksp-wallet-lib/unit_tests/transcript.rs
Normal file
71
crates/ksp-wallet-lib/unit_tests/transcript.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/transcript.rs
|
||||
// version: 1
|
||||
|
||||
use base64::Engine as _;
|
||||
|
||||
const FIXTURE: &[u8] = include_bytes!("../tests/fixtures/kspwallet_v1_wire_only.json");
|
||||
|
||||
#[test]
|
||||
fn state_transcript_matches_the_frozen_v1_wire_only_canary() -> ksp_core_lib::Result<()> {
|
||||
let envelope = match crate::KspWalletEnvelopeV1::parse_json(FIXTURE) {
|
||||
std::result::Result::Ok(envelope) => envelope,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let encoded = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(envelope.state_transcript());
|
||||
assert_eq!(
|
||||
encoded,
|
||||
"S1NQV0FMTEVULVYxLVNUQVRFAAABAAAAAAAAAAlLU1BXQUxMRVQAAgAAAAAAAAAEAAAAAQADAAAAAAAAACAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAQAAAAAAAAAAEBABEAAAAAAAAABHZpZXcAEgAAAAAAAAAQICEiIyQlJicoKSorLC0uLwEAAAAAAAAAABAQERITFBUWFxgZGhscHR4fAQEAAAAAAAAABW93bmVyAQIAAAAAAAAACGFyZ29uMmlkAQMAAAAAAAAABAAAABMBBAAAAAAAAAAEAAEAAAEFAAAAAAAAAAQAAAADAQYAAAAAAAAABAAAAAEBBwAAAAAAAAAQMDEyMzQ1Njc4OTo7PD0-PwEIAAAAAAAAABJ4Y2hhY2hhMjAtcG9seTEzMDUBCQAAAAAAAAAYUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnAQoAAAAAAAAAIICBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6fAgAAAAAAAAAADW93bmVyLWNvbnRyb2wCAQAAAAAAAAAEAAAAAQICAAAAAAAAABJ4Y2hhY2hhMjAtcG9seTEzMDUCAwAAAAAAAAAYkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanAgQAAAAAAAAAMMDBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7wIAAAAAAAAAAAhtZXRhZGF0YQIBAAAAAAAAAAQAAAABAgIAAAAAAAAAEnhjaGFjaGEyMC1wb2x5MTMwNQIDAAAAAAAAABioqaqrrK2ur7CxsrO0tba3uLm6u7y9vr8CBAAAAAAAAAAwEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_AgAAAAAAAAAABnNlY3JldAIBAAAAAAAAAAQAAAABAgIAAAAAAAAAEnhjaGFjaGEyMC1wb2x5MTMwNQIDAAAAAAAAABjAwcLDxMXGx8jJysvMzc7P0NHS09TV1tcCBAAAAAAAAABQQEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo8FAAAAAAAAAAAHZWQyNTUxOQ"
|
||||
);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slot_aad_matches_owner_and_view_canaries() -> ksp_core_lib::Result<()> {
|
||||
let envelope = match crate::KspWalletEnvelopeV1::parse_json(FIXTURE) {
|
||||
std::result::Result::Ok(envelope) => envelope,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let owner = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(envelope.owner_slot_aad());
|
||||
assert_eq!(
|
||||
owner,
|
||||
"S1NQV0FMTEVULVYxLUFBRC1PV05FUi1TTE9UAAABAAAAAAAAAAlLU1BXQUxMRVQAAgAAAAAAAAAEAAAAAQADAAAAAAAAACAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwEAAAAAAAAAABAQERITFBUWFxgZGhscHR4fAQEAAAAAAAAABW93bmVyAQIAAAAAAAAACGFyZ29uMmlkAQMAAAAAAAAABAAAABMBBAAAAAAAAAAEAAEAAAEFAAAAAAAAAAQAAAADAQYAAAAAAAAABAAAAAEBBwAAAAAAAAAQMDEyMzQ1Njc4OTo7PD0-PwEIAAAAAAAAABJ4Y2hhY2hhMjAtcG9seTEzMDU"
|
||||
);
|
||||
let view_aad_result = envelope.view_slot_aad();
|
||||
assert!(view_aad_result.is_some());
|
||||
let view_aad = match view_aad_result {
|
||||
std::option::Option::Some(view_aad) => view_aad,
|
||||
std::option::Option::None => return std::result::Result::Ok(()),
|
||||
};
|
||||
let view = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(view_aad);
|
||||
assert_eq!(
|
||||
view,
|
||||
"S1NQV0FMTEVULVYxLUFBRC1WSUVXLVNMT1QAAAEAAAAAAAAACUtTUFdBTExFVAACAAAAAAAAAAQAAAABAAMAAAAAAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAQAAAAAAAAAAECAhIiMkJSYnKCkqKywtLi8BAQAAAAAAAAAEdmlldwECAAAAAAAAAAhhcmdvbjJpZAEDAAAAAAAAAAQAAAATAQQAAAAAAAAABAAAgAABBQAAAAAAAAAEAAAABAEGAAAAAAAAAAQAAAABAQcAAAAAAAAAEEBBQkNERUZHSElKS0xNTk8BCAAAAAAAAAASeGNoYWNoYTIwLXBvbHkxMzA1"
|
||||
);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compartment_aad_is_domain_separated_by_kind() -> ksp_core_lib::Result<()> {
|
||||
let envelope = match crate::KspWalletEnvelopeV1::parse_json(FIXTURE) {
|
||||
std::result::Result::Ok(envelope) => envelope,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let owner_control = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(envelope.compartment_aad(crate::WalletCompartmentKindV1::OwnerControl));
|
||||
let metadata = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(envelope.compartment_aad(crate::WalletCompartmentKindV1::Metadata));
|
||||
let secret = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(envelope.compartment_aad(crate::WalletCompartmentKindV1::Secret));
|
||||
assert_eq!(
|
||||
owner_control,
|
||||
"S1NQV0FMTEVULVYxLUFBRC1PV05FUi1DT05UUk9MAAABAAAAAAAAAAlLU1BXQUxMRVQAAgAAAAAAAAAEAAAAAQADAAAAAAAAACAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwIAAAAAAAAAAA1vd25lci1jb250cm9sAgEAAAAAAAAABAAAAAECAgAAAAAAAAASeGNoYWNoYTIwLXBvbHkxMzA1"
|
||||
);
|
||||
assert_eq!(
|
||||
metadata,
|
||||
"S1NQV0FMTEVULVYxLUFBRC1NRVRBREFUQQAAAQAAAAAAAAAJS1NQV0FMTEVUAAIAAAAAAAAABAAAAAEAAwAAAAAAAAAgAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8CAAAAAAAAAAAIbWV0YWRhdGECAQAAAAAAAAAEAAAAAQICAAAAAAAAABJ4Y2hhY2hhMjAtcG9seTEzMDU"
|
||||
);
|
||||
assert_eq!(
|
||||
secret,
|
||||
"S1NQV0FMTEVULVYxLUFBRC1TRUNSRVQAAAEAAAAAAAAACUtTUFdBTExFVAACAAAAAAAAAAQAAAABAAMAAAAAAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAgAAAAAAAAAABnNlY3JldAIBAAAAAAAAAAQAAAABAgIAAAAAAAAAEnhjaGFjaGEyMC1wb2x5MTMwNQ"
|
||||
);
|
||||
assert_ne!(owner_control, metadata);
|
||||
assert_ne!(metadata, secret);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
126
crates/ksp-wallet-lib/unit_tests/wire.rs
Normal file
126
crates/ksp-wallet-lib/unit_tests/wire.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/wire.rs
|
||||
// version: 1
|
||||
|
||||
const FIXTURE: &[u8] = include_bytes!("../tests/fixtures/kspwallet_v1_wire_only.json");
|
||||
|
||||
#[test]
|
||||
fn strict_v1_fixture_parses_and_round_trips_semantically() -> ksp_core_lib::Result<()> {
|
||||
let parsed = match super::KspWalletEnvelopeV1::parse_json(FIXTURE) {
|
||||
std::result::Result::Ok(parsed) => parsed,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
assert_eq!(parsed.format_version(), crate::KSPWALLET_FORMAT_VERSION_V1);
|
||||
assert!(parsed.view_descriptor().enabled());
|
||||
assert_eq!(parsed.owner_slot().role(), super::WalletKeySlotRoleV1::Owner);
|
||||
assert_eq!(parsed.view_slot().map(super::WalletKeySlotV1::role), std::option::Option::Some(super::WalletKeySlotRoleV1::View));
|
||||
assert_eq!(parsed.owner_control().payload_version(), 1);
|
||||
assert_eq!(parsed.metadata().payload_version(), 1);
|
||||
assert_eq!(parsed.secret().payload_version(), 1);
|
||||
|
||||
let serialized = match parsed.to_json_bytes() {
|
||||
std::result::Result::Ok(serialized) => serialized,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let reparsed = match super::KspWalletEnvelopeV1::parse_json(serialized.as_slice()) {
|
||||
std::result::Result::Ok(reparsed) => reparsed,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
assert_eq!(parsed, reparsed);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_format_version_is_rejected_before_v1_shape_validation() {
|
||||
let source = std::string::String::from_utf8_lossy(FIXTURE).replace("\"format_version\": 1", "\"format_version\": 2");
|
||||
let result = super::KspWalletEnvelopeV1::parse_json(source.as_bytes());
|
||||
assert!(result.is_err());
|
||||
let error = match result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_FORMAT_VERSION_UNSUPPORTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_top_level_field_is_rejected() {
|
||||
let source = std::string::String::from_utf8_lossy(FIXTURE).replacen("{", "{\n \"unexpected\": true,", 1);
|
||||
let result = super::KspWalletEnvelopeV1::parse_json(source.as_bytes());
|
||||
assert!(result.is_err());
|
||||
let error = match result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padded_or_noncanonical_base64url_is_rejected() {
|
||||
let source = std::string::String::from_utf8_lossy(FIXTURE)
|
||||
.replace("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8\"", "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"");
|
||||
let result = super::KspWalletEnvelopeV1::parse_json(source.as_bytes());
|
||||
assert!(result.is_err());
|
||||
let error = match result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enabled_view_descriptor_must_match_the_view_slot() {
|
||||
let source =
|
||||
std::string::String::from_utf8_lossy(FIXTURE).replacen("\"slot_id\": \"ICEiIyQlJicoKSorLC0uLw\"", "\"slot_id\": \"EBESExQVFhcYGRobHB0eHw\"", 1);
|
||||
let result = super::KspWalletEnvelopeV1::parse_json(source.as_bytes());
|
||||
assert!(result.is_err());
|
||||
let error = match result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_or_pathological_kdf_parameters_are_rejected_before_crypto() {
|
||||
let zero_source = std::string::String::from_utf8_lossy(FIXTURE).replacen("\"memory_kib\": 65536", "\"memory_kib\": 0", 1);
|
||||
let zero_result = super::KspWalletEnvelopeV1::parse_json(zero_source.as_bytes());
|
||||
assert!(zero_result.is_err());
|
||||
let zero_error = match zero_result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(zero_error.code(), crate::ERROR_CODE_CRYPTO_PARAMETERS_INVALID);
|
||||
|
||||
let high_source = std::string::String::from_utf8_lossy(FIXTURE).replacen("\"memory_kib\": 65536", "\"memory_kib\": 1048577", 1);
|
||||
let high_result = super::KspWalletEnvelopeV1::parse_json(high_source.as_bytes());
|
||||
assert!(high_result.is_err());
|
||||
let high_error = match high_result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(high_error.code(), crate::ERROR_CODE_CRYPTO_PARAMETERS_INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oversized_document_is_rejected_before_json_or_crypto() {
|
||||
let oversized = std::vec![b' '; crate::KSPWALLET_MAX_FILE_BYTES + 1];
|
||||
let result = super::KspWalletEnvelopeV1::parse_json(oversized.as_slice());
|
||||
assert!(result.is_err());
|
||||
let error = match result {
|
||||
std::result::Result::Err(error) => error,
|
||||
std::result::Result::Ok(_) => return,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn envelope_debug_does_not_render_ciphertext_contents() -> ksp_core_lib::Result<()> {
|
||||
let parsed = match super::KspWalletEnvelopeV1::parse_json(FIXTURE) {
|
||||
std::result::Result::Ok(parsed) => parsed,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rendered = format!("{parsed:?}");
|
||||
assert!(rendered.contains("ciphertext_bytes"));
|
||||
assert!(!rendered.contains("gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp8"));
|
||||
assert!(!rendered.contains("wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v"));
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
Reference in New Issue
Block a user