100 lines
4.0 KiB
C++
100 lines
4.0 KiB
C++
#include "MotaContainer.h"
|
|
#include "MerkleTree.h"
|
|
#include "Multihash.h"
|
|
#include "OtaByteIO.h"
|
|
#include <string.h>
|
|
|
|
namespace mesh {
|
|
namespace ota {
|
|
|
|
bool MotaManifest::is_approved() const {
|
|
return approval && memcmp(approval, APPROVAL_YES, 4) == 0;
|
|
}
|
|
|
|
// Fixed-layout parse (docs/ota_protocol.md Section 4): every field sits at a constant offset - base_hash(8),
|
|
// signer_pubkey(32) and signature(64) are ALWAYS present (zero-filled when not applicable), so there are
|
|
// no conditionals. Only leaves[]/payload (after `approval`) is variable, read by the caller. The signature
|
|
// always covers manifest[0, MOTA_SIGNED_LEN). Returns false on any over-read or bad format_ver.
|
|
static bool parse_manifest_fields(ByteReader& r, MotaManifest& out) {
|
|
out.format_ver = r.u8();
|
|
if (out.format_ver != MOTA_FORMAT_VER) return false;
|
|
out.flags = r.u8();
|
|
out.hash_algo = r.u8();
|
|
out.target_id = r.u32();
|
|
out.fw_version = r.u32();
|
|
out.image_size = r.u32();
|
|
out.payload_size = r.u32();
|
|
out.block_size_log2 = r.u8();
|
|
out.merkle_root = r.take(4);
|
|
out.image_hash = r.take(32);
|
|
out.codec_id = r.u8();
|
|
out.hw_id = r.take(32); // 32-byte NUL-padded hardware tag (signed)
|
|
out.base_hash = r.take(8); // always present (zero for a full image)
|
|
out.signer_pubkey = r.take(32); // always present (zero when unsigned)
|
|
out.signed_len = MOTA_SIGNED_LEN; // signature always covers manifest[0, 129)
|
|
out.signature = r.take(64); // always present (zero when unsigned)
|
|
out.approval = r.take(4);
|
|
if (!r.ok) return false;
|
|
if (out.block_size_log2 == 0 || out.block_size_log2 > 24 || out.payload_size == 0) return false;
|
|
out.block_count = (out.payload_size + out.block_size() - 1) / out.block_size();
|
|
// block_idx is uint16 on the wire; capping here also keeps block_count*4 (leaves length) from overflowing.
|
|
return out.block_count != 0 && out.block_count <= 0xFFFFu;
|
|
}
|
|
|
|
bool mota_parse(const uint8_t* buf, uint32_t len, MotaManifest& out) {
|
|
out = MotaManifest();
|
|
if (len < 4 + 4 + 5) return false;
|
|
if (memcmp(buf, MOTA_MAGIC, 4) != 0) return false;
|
|
if (memcmp(buf + len - 5, MOTA_TRAILER, 5) != 0) return false;
|
|
if (rd_u32le(buf + 4) != len) return false; // MOTA_TOTAL_SIZE must equal the actual length
|
|
|
|
ByteReader r(buf, len - 5); // everything up to (not incl.) the trailer
|
|
r.skip(4 + 4); // MAGIC + MOTA_TOTAL_SIZE (already validated)
|
|
out.manifest_start = buf + 8;
|
|
if (!parse_manifest_fields(r, out)) return false;
|
|
out.leaves = r.take(out.block_count * 4);
|
|
out.payload = r.take(out.payload_size);
|
|
if (!r.ok) return false;
|
|
return r.pos() == len - 5; // payload must end exactly at the trailer
|
|
}
|
|
|
|
bool mota_parse_manifest(const uint8_t* mf, uint32_t len, MotaManifest& out) {
|
|
out = MotaManifest();
|
|
out.manifest_start = mf;
|
|
ByteReader r(mf, len); // a standalone manifest = container bytes [8, leaves)
|
|
return parse_manifest_fields(r, out);
|
|
}
|
|
|
|
bool mota_check_root(const MotaManifest& m) {
|
|
if (!m.leaves || m.block_count == 0) return false;
|
|
uint8_t root[4];
|
|
merkle_root(root, m.leaves, m.block_count);
|
|
return memcmp(root, m.merkle_root, 4) == 0;
|
|
}
|
|
|
|
bool mota_check_payload(const MotaManifest& m) {
|
|
if (!m.payload || !m.leaves || m.block_count == 0) return false;
|
|
uint32_t off = 0;
|
|
uint32_t bs = m.block_size();
|
|
for (uint32_t i = 0; i < m.block_count; i++) {
|
|
if (off >= m.payload_size) return false;
|
|
uint32_t len = m.payload_size - off;
|
|
if (len > bs) len = bs;
|
|
uint8_t leaf[4];
|
|
merkle_leaf(leaf, m.payload + off, len);
|
|
if (memcmp(leaf, m.leaves + i * 4, 4) != 0) return false;
|
|
off += len;
|
|
}
|
|
return off == m.payload_size;
|
|
}
|
|
|
|
bool mota_check_image_hash_full(const MotaManifest& m) {
|
|
if (!m.is_full() || !m.payload || !m.image_hash) return false;
|
|
uint8_t h[32];
|
|
mh32(h, m.payload, m.payload_size);
|
|
return memcmp(h, m.image_hash, 32) == 0;
|
|
}
|
|
|
|
} // namespace ota
|
|
} // namespace mesh
|