66#include " VMPilot_crypto.hpp"
77#include " binding/inner_partition.hpp"
88#include " binding/resolved_profile.hpp"
9+ #include " cbor/schema.hpp"
910#include " cbor/strict.hpp"
1011#include " eh_guard.hpp"
1112#include " vm/domain_labels.hpp"
@@ -15,9 +16,16 @@ namespace VMPilot::Cbor {
1516template <>
1617struct CborConsumerTraits <VMPilot::Runtime::Binding::UnitAcceptError> {
1718 using E = VMPilot::Runtime::Binding::UnitAcceptError;
18- static constexpr E missing_field = E::MissingCoreField;
19+ static constexpr E missing_field = E::MissingCoreField;
1920 static constexpr E wrong_field_type = E::WrongFieldType;
20- static constexpr E wrong_hash_size = E::WrongHashSize;
21+ static constexpr E wrong_hash_size = E::WrongHashSize;
22+ // Schema-side defaults. parse_payload_identity is still imperative
23+ // because its caller needs to route a not-a-map failure to
24+ // UnitDescriptorMalformed vs UnitBindingRecordMalformed by
25+ // context; the constants below are the generic fall-backs
26+ // used only by the top-level schema calls.
27+ static constexpr E bad_cbor = E::UnitDescriptorMalformed;
28+ static constexpr E not_a_map = E::UnitDescriptorMalformed;
2129};
2230} // namespace VMPilot::Cbor
2331
@@ -73,11 +81,6 @@ inline tl::unexpected<UnitAcceptError> err(UnitAcceptError e) noexcept {
7381 return tl::make_unexpected (e);
7482}
7583
76- template <typename E>
77- tl::unexpected<UnitAcceptError> err_as (UnitAcceptError e, const E&) noexcept {
78- return tl::make_unexpected (e);
79- }
80-
8184bool hash_equals (const std::array<std::uint8_t , 32 >& a,
8285 const std::array<std::uint8_t , 32 >& b) noexcept {
8386 return std::memcmp (a.data (), b.data (), 32 ) == 0 ;
@@ -115,9 +118,6 @@ UnitAcceptError map_eh_contract_error(
115118// {MissingCoreField, WrongFieldType, WrongHashSize} triple, so the
116119// per-call error customisation point was dead weight.
117120
118- inline auto require_text (const Value& m, std::uint64_t k) noexcept {
119- return VMPilot::Cbor::require_text<UnitAcceptError>(m, k);
120- }
121121inline auto require_uint (const Value& m, std::uint64_t k) noexcept {
122122 return VMPilot::Cbor::require_uint<UnitAcceptError>(m, k);
123123}
@@ -144,77 +144,68 @@ tl::expected<PayloadIdentity, UnitAcceptError> parse_payload_identity(
144144
145145tl::expected<UnitDescriptor, UnitAcceptError> parse_unit_descriptor_bytes (
146146 const std::vector<std::uint8_t >& bytes) noexcept {
147+ using namespace VMPilot ::Cbor::Schema;
147148 auto tree_or = parse_strict (bytes.data (), bytes.size ());
148149 if (!tree_or)
149150 return err (UnitAcceptError::UnitDescriptorMalformed);
150151 const Value& tree = *tree_or;
151- if (tree.kind () != Value::Kind::Map)
152- return err (UnitAcceptError::UnitDescriptorMalformed);
153152
154- VMPILOT_TRY_ASSIGN (ver, require_text (tree, kUd_DescriptorVersion ));
155- VMPILOT_TRY_ASSIGN (uid, require_text (tree, kUd_UnitId ));
156- VMPILOT_TRY_ASSIGN (uih, require_hash32 (tree, kUd_UnitIdentityHash ));
157- VMPILOT_TRY_ASSIGN (fam, require_text (tree, kUd_FamilyId ));
158- VMPILOT_TRY_ASSIGN (pol, require_text (tree, kUd_RequestedPolicyId ));
159- VMPILOT_TRY_ASSIGN (prof, require_text (tree, kUd_ResolvedFamilyProfileId ));
160- VMPILOT_TRY_ASSIGN (ubr, require_text (tree, kUd_UnitBindingRecordId ));
161-
162- auto fam_enum = VMPilot::DomainLabels::parse_family_id (fam);
163- if (!fam_enum)
164- return err (UnitAcceptError::UnknownFamilyId);
165- auto pol_enum = VMPilot::DomainLabels::parse_policy_id (pol);
166- if (!pol_enum)
167- return err (UnitAcceptError::UnknownPolicyId);
153+ const auto schema = std::make_tuple (
154+ TextField<UnitDescriptor>{kUd_DescriptorVersion ,
155+ &UnitDescriptor::descriptor_version},
156+ TextField<UnitDescriptor>{kUd_UnitId , &UnitDescriptor::unit_id},
157+ HashField<UnitDescriptor>{kUd_UnitIdentityHash ,
158+ &UnitDescriptor::unit_identity_hash},
159+ EnumTextField<UnitDescriptor,
160+ VMPilot::DomainLabels::FamilyId, UnitAcceptError>{
161+ kUd_FamilyId , &UnitDescriptor::family_id,
162+ UnitAcceptError::UnknownFamilyId},
163+ EnumTextField<UnitDescriptor,
164+ VMPilot::DomainLabels::PolicyId, UnitAcceptError>{
165+ kUd_RequestedPolicyId , &UnitDescriptor::requested_policy_id,
166+ UnitAcceptError::UnknownPolicyId},
167+ TextField<UnitDescriptor>{kUd_ResolvedFamilyProfileId ,
168+ &UnitDescriptor::resolved_family_profile_id},
169+ TextField<UnitDescriptor>{kUd_UnitBindingRecordId ,
170+ &UnitDescriptor::unit_binding_record_id}
171+ );
172+ auto parsed = parse_schema<UnitDescriptor, UnitAcceptError>(tree, schema);
173+ if (!parsed) return err (parsed.error ());
168174
169175 const Value* pid_v = tree.find_by_uint_key (kUd_PayloadIdentity );
170176 if (pid_v == nullptr )
171177 return err (UnitAcceptError::MissingCoreField);
172- VMPILOT_TRY_ASSIGN (
173- pid, parse_payload_identity (*pid_v,
174- UnitAcceptError::UnitDescriptorMalformed));
175-
176- UnitDescriptor out;
177- out.descriptor_version = std::string (ver);
178- out.unit_id = std::string (uid);
179- out.unit_identity_hash = uih;
180- out.family_id = *fam_enum;
181- out.requested_policy_id = *pol_enum;
182- out.resolved_family_profile_id = std::string (prof);
183- out.payload_identity = pid;
184- out.unit_binding_record_id = std::string (ubr);
185- return out;
178+ auto pid = parse_payload_identity (*pid_v,
179+ UnitAcceptError::UnitDescriptorMalformed);
180+ if (!pid) return err (pid.error ());
181+ parsed->payload_identity = *pid;
182+ return parsed;
186183}
187184
188185// ─── UnitBindingAuth + UBR ──────────────────────────────────────────────
189186
190187tl::expected<UnitBindingAuth, UnitAcceptError> parse_binding_auth (
191188 const Value& auth_v) noexcept {
189+ using namespace VMPilot ::Cbor::Schema;
192190 if (auth_v.kind () != Value::Kind::Map)
193191 return err (UnitAcceptError::UnitBindingAuthMalformed);
194192
195- auto kind = require_text (auth_v, kAuth_Kind );
196- auto ubt_hash = require_hash32 (auth_v, kAuth_UnitBindingTableHash );
197- auto inclusion = require_uint (auth_v, kAuth_InclusionIndex );
198- auto rec_hash = require_hash32 (auth_v, kAuth_RecordHash );
199- if (!kind)
200- return err (kind.error ());
201- if (!ubt_hash)
202- return err (ubt_hash.error ());
203- if (!inclusion)
204- return err (inclusion.error ());
205- if (!rec_hash)
206- return err (rec_hash.error ());
207-
208- if (*kind != kAuthKindPackageSignedUnitInclusionV1 ) {
193+ const auto schema = std::make_tuple (
194+ TextField<UnitBindingAuth>{kAuth_Kind , &UnitBindingAuth::kind},
195+ HashField<UnitBindingAuth>{kAuth_UnitBindingTableHash ,
196+ &UnitBindingAuth::unit_binding_table_hash},
197+ UintField<UnitBindingAuth>{kAuth_InclusionIndex ,
198+ &UnitBindingAuth::inclusion_index},
199+ HashField<UnitBindingAuth>{kAuth_RecordHash ,
200+ &UnitBindingAuth::record_hash}
201+ );
202+ auto parsed = parse_schema<UnitBindingAuth, UnitAcceptError>(auth_v, schema);
203+ if (!parsed) return err (parsed.error ());
204+
205+ if (parsed->kind != kAuthKindPackageSignedUnitInclusionV1 ) {
209206 return err (UnitAcceptError::UnitBindingAuthMalformed);
210207 }
211-
212- UnitBindingAuth out;
213- out.kind = std::move (*kind);
214- out.unit_binding_table_hash = *ubt_hash;
215- out.inclusion_index = *inclusion;
216- out.record_hash = *rec_hash;
217- return out;
208+ return parsed;
218209}
219210
220211// Parse a single UBT entry (CBOR array[2] = [canonical_without_auth_bytes,
@@ -238,47 +229,46 @@ tl::expected<UnitBindingRecord, UnitAcceptError> parse_unit_binding_record(
238229 return err (UnitAcceptError::UnitBindingAuthMalformed);
239230 }
240231
232+ using namespace VMPilot ::Cbor::Schema;
241233 const auto & canonical_bytes = canonical_v.as_bytes ();
242234 auto inner_or =
243235 parse_strict (canonical_bytes.data (), canonical_bytes.size ());
244236 if (!inner_or)
245237 return err (UnitAcceptError::UnitBindingRecordMalformed);
246238 const Value& ubr_v = *inner_or;
247- if (ubr_v.kind () != Value::Kind::Map) {
248- return err (UnitAcceptError::UnitBindingRecordMalformed);
249- }
250239
251- auto id = require_text (ubr_v, kUbr_UnitBindingRecordId );
252- auto uih = require_hash32 (ubr_v, kUbr_UnitIdentityHash );
253- auto udh = require_hash32 (ubr_v, kUbr_UnitDescriptorHash );
254- auto fam = require_text (ubr_v, kUbr_FamilyId );
255- auto pol = require_text (ubr_v, kUbr_RequestedPolicyId );
256- auto prof = require_text (ubr_v, kUbr_ResolvedFamilyProfileId );
257- auto pch = require_hash32 (ubr_v, kUbr_ResolvedFamilyProfileContentHash );
258- auto epoch = require_uint (ubr_v, kUbr_AntiDowngradeEpoch );
259- if (!id)
260- return err (id.error ());
261- if (!uih)
262- return err (uih.error ());
263- if (!udh)
264- return err (udh.error ());
265- if (!fam)
266- return err (fam.error ());
267- if (!pol)
268- return err (pol.error ());
269- if (!prof)
270- return err (prof.error ());
271- if (!pch)
272- return err (pch.error ());
273- if (!epoch)
274- return err (epoch.error ());
275-
276- auto fam_enum = VMPilot::DomainLabels::parse_family_id (*fam);
277- if (!fam_enum)
278- return err (UnitAcceptError::UnknownFamilyId);
279- auto pol_enum = VMPilot::DomainLabels::parse_policy_id (*pol);
280- if (!pol_enum)
281- return err (UnitAcceptError::UnknownPolicyId);
240+ const auto schema = std::make_tuple (
241+ TextField<UnitBindingRecord>{kUbr_UnitBindingRecordId ,
242+ &UnitBindingRecord::unit_binding_record_id},
243+ HashField<UnitBindingRecord>{kUbr_UnitIdentityHash ,
244+ &UnitBindingRecord::unit_identity_hash},
245+ HashField<UnitBindingRecord>{kUbr_UnitDescriptorHash ,
246+ &UnitBindingRecord::unit_descriptor_hash},
247+ EnumTextField<UnitBindingRecord,
248+ VMPilot::DomainLabels::FamilyId, UnitAcceptError>{
249+ kUbr_FamilyId , &UnitBindingRecord::family_id,
250+ UnitAcceptError::UnknownFamilyId},
251+ EnumTextField<UnitBindingRecord,
252+ VMPilot::DomainLabels::PolicyId, UnitAcceptError>{
253+ kUbr_RequestedPolicyId , &UnitBindingRecord::requested_policy_id,
254+ UnitAcceptError::UnknownPolicyId},
255+ TextField<UnitBindingRecord>{kUbr_ResolvedFamilyProfileId ,
256+ &UnitBindingRecord::resolved_family_profile_id},
257+ HashField<UnitBindingRecord>{kUbr_ResolvedFamilyProfileContentHash ,
258+ &UnitBindingRecord::resolved_family_profile_content_hash},
259+ UintField<UnitBindingRecord>{kUbr_AntiDowngradeEpoch ,
260+ &UnitBindingRecord::anti_downgrade_epoch}
261+ );
262+ auto parsed = parse_schema<UnitBindingRecord, UnitAcceptError>(ubr_v, schema);
263+ if (!parsed) {
264+ // parse_schema emits NotAMap via the default trait constants,
265+ // which route to UnitDescriptorMalformed. Remap to the UBR
266+ // variant here so the error keeps pointing at the right layer.
267+ if (ubr_v.kind () != Value::Kind::Map) {
268+ return err (UnitAcceptError::UnitBindingRecordMalformed);
269+ }
270+ return err (parsed.error ());
271+ }
282272
283273 const Value* pid_v = ubr_v.find_by_uint_key (kUbr_PayloadIdentity );
284274 if (pid_v == nullptr )
@@ -287,10 +277,12 @@ tl::expected<UnitBindingRecord, UnitAcceptError> parse_unit_binding_record(
287277 *pid_v, UnitAcceptError::UnitBindingRecordMalformed);
288278 if (!pid)
289279 return err (pid.error ());
280+ parsed->payload_identity = *pid;
290281
291282 auto auth = parse_binding_auth (auth_v);
292283 if (!auth)
293284 return err (auth.error ());
285+ parsed->binding_auth = std::move (*auth);
294286
295287 // Record hash must commit to the exact canonical bytes we just parsed.
296288 // Without this the wrapper's element [0] could be swapped for any
@@ -299,22 +291,10 @@ tl::expected<UnitBindingRecord, UnitAcceptError> parse_unit_binding_record(
299291 const auto computed_record_hash =
300292 domain_hash_sha256 (VMPilot::DomainLabels::Hash::UnitBindingRecord,
301293 canonical_bytes.data (), canonical_bytes.size ());
302- if (!hash_equals (computed_record_hash, auth-> record_hash )) {
294+ if (!hash_equals (computed_record_hash, parsed-> binding_auth . record_hash )) {
303295 return err (UnitAcceptError::UnitBindingRecordRecordHashMismatch);
304296 }
305-
306- UnitBindingRecord out;
307- out.unit_binding_record_id = std::move (*id);
308- out.unit_identity_hash = *uih;
309- out.unit_descriptor_hash = *udh;
310- out.family_id = *fam_enum;
311- out.requested_policy_id = *pol_enum;
312- out.resolved_family_profile_id = std::move (*prof);
313- out.resolved_family_profile_content_hash = *pch;
314- out.payload_identity = *pid;
315- out.anti_downgrade_epoch = *epoch;
316- out.binding_auth = std::move (*auth);
317- return out;
297+ return parsed;
318298}
319299
320300// ─── Table lookups ──────────────────────────────────────────────────────
0 commit comments