use cbor_diag::parse_diag;
use suit_validator::suit_decode;
const MANIFEST_TEMPLATE: &str = r#"
107({
/ authentication-wrapper / 2:<< [
/ digest: / << [
/ algorithm-id / __AUTH_ALG__ / "sha256" /,
/ digest-bytes /
h'__DIGEST__'
] >>,
/ signature: / << 18([
/ protected / << {
/ alg / 1:-7 / "ES256" /
} >>,
/ unprotected / {
},
/ payload / null / nil /,
/ signature / h'__SIGNATURE__'
]) >>
] >>,
/ manifest / 3:<< {
/ manifest-version / 1:__VERSION__,
/ manifest-sequence-number / 2:1,
/ common / 3:<< {
/ components / 2:[
__COMPONENTS__
],
/ shared-sequence / 4:<< [
__SHARED_SEQ_BODY__
] >>
} >>,
/ validate / 7:<< [
/ condition-image-match / 3,__POLICY_VALIDATE__
] >>,
/ install / 20:<< [
/ directive-override-parameters / 20,{
/ uri / 21:"__INSTALL_URI__"
},
/ directive-fetch / 21,__FETCH_PARAM__,
/ condition-image-match / 3,__POLICY_INSTALL__
] >>
} >>
})"#;
const DEFAULT_AUTH_ALG: i32 = -16;
const DEFAULT_DIGEST: &str = "1f2e7acca0dc2786f2fe4eb947f50873a6a3cfaa98866c5b02e621f42074daf2";
const DEFAULT_SIGNATURE: &str = "27a3d7986eddcc1bee04e1436746408c308ed3c15ac590a1ca0cf96f85671ccac216cb9a1497fc59e21c15f33c95cf75203e25c287b31a57d6cd2ef950b27a7a";
const DEFAULT_VERSION: u64 = 1;
const DEFAULT_COMPONENTS: &str = "[ [h'00'] ]"; const DEFAULT_SHARED_SEQ_BODY: &str = r#"
/ directive-override-parameters / 20,{
/ vendor-id /
1:h'fa6b4a53d5ad5fdfbe9de663e4d41ffe' / fa6b4a53-d5ad-5fdf-be9d-e663e4d41ffe /,
/ class-id /
2:h'1492af1425695e48bf429b2d51f2ab45' / 1492af14-2569-5e48-bf42-9b2d51f2ab45 /,
/ image-digest / 3:<< [
/ algorithm-id / -16 / "sha256" /,
/ digest-bytes /
h'00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210'
] >>,
/ image-size / 14:34768
},
/ condition-vendor-identifier / 1,15,
/ condition-class-identifier / 2,15
"#;
const DEFAULT_IMAGE_DIGEST: &str =
"00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210";
const DEFAULT_IMAGE_SIZE: u64 = 34768;
const DEFAULT_POLICY_VALIDATE: u8 = 15;
const DEFAULT_INSTALL_URI: &str = "http://example.com/file.bin";
const DEFAULT_FETCH_PARAM: u64 = 2;
const DEFAULT_POLICY_INSTALL: u8 = 15;
#[derive(Clone, Debug)]
pub enum ManifestChange {
Version(u64),
AuthAlg(i32),
Digest(&'static str),
EmptySharedSequence,
EmptyComponents,
RemoveImageSize,
VendorIdTooLong(&'static str),
PolicyValidate(u8),
PolicyInstall(u8),
RemoveInstallUri,
BadSignature,
}
pub fn build_manifest_bytes_with_changes(changes: &[ManifestChange]) -> Vec<u8> {
let mut auth_alg = DEFAULT_AUTH_ALG;
let mut digest = DEFAULT_DIGEST.to_string();
let mut signature = DEFAULT_SIGNATURE.to_string();
let mut version = DEFAULT_VERSION;
let mut components = DEFAULT_COMPONENTS.to_string();
let mut shared_seq_body = DEFAULT_SHARED_SEQ_BODY.to_string();
let image_digest = DEFAULT_IMAGE_DIGEST.to_string();
let image_size = DEFAULT_IMAGE_SIZE;
let mut policy_validate = DEFAULT_POLICY_VALIDATE;
let install_uri = DEFAULT_INSTALL_URI.to_string();
let fetch_param = DEFAULT_FETCH_PARAM;
let mut policy_install = DEFAULT_POLICY_INSTALL;
let mut vendor_id = "fa6b4a53d5ad5fdfbe9de663e4d41ffe".to_string();
let class_id = "1492af1425695e48bf429b2d51f2ab45".to_string();
let mut should_remove_image_size = false;
let mut should_remove_install_uri = false;
for c in changes {
match c {
ManifestChange::Version(v) => version = *v,
ManifestChange::AuthAlg(a) => auth_alg = *a,
ManifestChange::Digest(d) => digest = d.to_string(),
ManifestChange::EmptySharedSequence => shared_seq_body = String::new(),
ManifestChange::EmptyComponents => components = String::from(""),
ManifestChange::RemoveImageSize => should_remove_image_size = true,
ManifestChange::VendorIdTooLong(v) => vendor_id = v.to_string(),
ManifestChange::PolicyValidate(p) => policy_validate = *p,
ManifestChange::PolicyInstall(p) => policy_install = *p,
ManifestChange::RemoveInstallUri => should_remove_install_uri = true,
ManifestChange::BadSignature => signature = "00".to_string(),
}
}
if shared_seq_body.is_empty() {
shared_seq_body = String::new();
} else {
shared_seq_body = format!(
r#"
/ directive-override-parameters / 20,{{
/ vendor-id /
1:h'{vendor_id}' / {vendor_id} /,
/ class-id /
2:h'{class_id}' / {class_id} /,
/ image-digest / 3:<< [
/ algorithm-id / -16 / "sha256" /,
/ digest-bytes /
h'{image_digest}'
] >>,
/ image-size / 14:{image_size}
}},
/ condition-vendor-identifier / 1,15,
/ condition-class-identifier / 2,15
"#
);
}
let components_str = if components.is_empty() {
String::from("")
} else {
components
};
let mut diag = MANIFEST_TEMPLATE
.replace("__AUTH_ALG__", &auth_alg.to_string())
.replace("__DIGEST__", &digest)
.replace("__SIGNATURE__", &signature)
.replace("__VERSION__", &version.to_string())
.replace("__COMPONENTS__", &components_str)
.replace("__SHARED_SEQ_BODY__", &shared_seq_body)
.replace("__IMAGE_DIGEST__", &image_digest)
.replace("__IMAGE_SIZE__", &image_size.to_string())
.replace("__POLICY_VALIDATE__", &policy_validate.to_string())
.replace("__INSTALL_URI__", &install_uri)
.replace("__FETCH_PARAM__", &fetch_param.to_string())
.replace("__POLICY_INSTALL__", &policy_install.to_string());
if should_remove_image_size {
diag = diag.replace(
"/ image-size / 14:__IMAGE_SIZE__",
"/ image-size / null / nil /",
);
}
if should_remove_install_uri {
diag = diag.replace("/ uri / 21:\"__INSTALL_URI__\"", "/ uri / null / nil /");
}
parse_diag(&diag)
.unwrap_or_else(|_| panic!("template diag must parse\n---diag---\n{diag}"))
.to_bytes()
}
pub fn build_manifest_bytes_with_change(change: ManifestChange) -> Vec<u8> {
build_manifest_bytes_with_changes(&[change])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::setup::MockCrypto;
use suit_validator::handler::GenericStartHandler;
#[test]
fn manifest_bad_version_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::Version(2));
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for bad version"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_unsupported_digest_alg_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::AuthAlg(-18)); let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for unsupported digest alg"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_bad_digest_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::Digest("deadbeef"));
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for bad digest"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_bad_signature_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::BadSignature);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for bad signature"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_empty_shared_sequence_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::EmptySharedSequence);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for empty shared sequence"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_empty_components_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::EmptyComponents);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for empty components"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_missing_image_size_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::RemoveImageSize);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for missing image size"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_vendor_id_too_long_should_fail() {
let long_vendor = "00112233445566778899aabbccddeeff00";
let bytes = build_manifest_bytes_with_change(ManifestChange::VendorIdTooLong(long_vendor));
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for long vendor id"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_policy_invalid_should_fail() {
let bytes = build_manifest_bytes_with_changes(&[
ManifestChange::PolicyValidate(255),
ManifestChange::PolicyInstall(255),
]);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for invalid policy"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_install_without_uri_should_fail() {
let bytes = build_manifest_bytes_with_change(ManifestChange::RemoveInstallUri);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for install without uri"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
#[test]
fn manifest_bad_digest_and_signature_should_fail() {
let bytes = build_manifest_bytes_with_changes(&[
ManifestChange::Digest("deadbeef"),
ManifestChange::BadSignature,
]);
let mut handler = GenericStartHandler {
on_envelope: |_env| panic!("Should not reach handler for bad manifest"),
on_manifest: |_manif| panic!("Should not happend since we are decoding an envelop"),
};
assert!(suit_decode(&bytes, &mut handler, &mut MockCrypto).is_err());
}
}