1use alloc::string::String;
2use alloc::vec::Vec;
3
4use anyhow::{bail, Context, Result};
5use scale::{Decode, Encode, Input, Output};
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "borsh_schema")]
9use borsh::BorshSchema;
10#[cfg(feature = "borsh")]
11use borsh::{BorshDeserialize, BorshSerialize};
12
13use crate::constants::*;
14
15#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
16#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
17#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
18pub struct Data<T> {
19 pub data: Vec<u8>,
20 _marker: core::marker::PhantomData<T>,
21}
22
23impl<T> Data<T> {
24 pub fn new(data: Vec<u8>) -> Self {
25 Self {
26 data,
27 _marker: core::marker::PhantomData,
28 }
29 }
30}
31
32impl<T> Serialize for Data<T> {
33 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
34 serde_bytes::serialize(&self.data, serializer)
35 }
36}
37
38impl<'de, T> Deserialize<'de> for Data<T> {
39 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
40 let data = serde_bytes::deserialize(deserializer)?;
41 Ok(Data {
42 data,
43 _marker: core::marker::PhantomData,
44 })
45 }
46}
47
48impl<T: Decode + Into<u64>> Decode for Data<T> {
49 fn decode<I: Input>(input: &mut I) -> Result<Self, scale::Error> {
50 const MAX_DATA_LEN: u64 = 1_048_576; let len = T::decode(input)?;
53 let len_u64 = len.into();
54 if len_u64 > MAX_DATA_LEN {
55 return Err(scale::Error::from("Data length exceeds maximum"));
56 }
57
58 let mut data = vec![0u8; len_u64 as usize];
59 input.read(&mut data)?;
60 Ok(Data {
61 data,
62 _marker: core::marker::PhantomData,
63 })
64 }
65}
66
67impl Encode for Data<u16> {
68 fn encode_to<O: Output + ?Sized>(&self, output: &mut O) {
69 let len = self.data.len() as u16;
70 len.encode_to(output);
71 output.write(&self.data);
72 }
73}
74
75impl Encode for Data<u32> {
76 fn encode_to<O: Output + ?Sized>(&self, output: &mut O) {
77 let len = self.data.len() as u32;
78 len.encode_to(output);
79 output.write(&self.data);
80 }
81}
82
83#[derive(
84 Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
85)]
86#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
87#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
88pub struct Header {
89 pub version: u16,
90 pub attestation_key_type: u16,
91 pub tee_type: u32,
92 pub qe_svn: u16,
93 pub pce_svn: u16,
94 #[serde(with = "serde_bytes")]
95 pub qe_vendor_id: [u8; 16],
96 #[serde(with = "serde_bytes")]
97 pub user_data: [u8; 20],
98}
99
100impl Header {
101 pub fn is_sgx(&self) -> bool {
102 self.tee_type == TEE_TYPE_SGX
103 }
104}
105
106#[derive(Decode, Encode, Debug)]
107pub struct Body {
108 pub body_type: u16,
109 pub size: u32,
110}
111
112#[derive(
113 Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
114)]
115#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
116#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
117pub struct EnclaveReport {
118 #[serde(with = "serde_bytes")]
119 pub cpu_svn: [u8; 16],
120 pub misc_select: u32,
121 #[serde(with = "serde_bytes")]
122 pub reserved1: [u8; 28],
123 #[serde(with = "serde_bytes")]
124 pub attributes: [u8; 16],
125 #[serde(with = "serde_bytes")]
126 pub mr_enclave: [u8; 32],
127 #[serde(with = "serde_bytes")]
128 pub reserved2: [u8; 32],
129 #[serde(with = "serde_bytes")]
130 pub mr_signer: [u8; 32],
131 #[serde(with = "serde_bytes")]
132 pub reserved3: [u8; 96],
133 pub isv_prod_id: u16,
134 pub isv_svn: u16,
135 #[serde(with = "serde_bytes")]
136 pub reserved4: [u8; 60],
137 #[serde(with = "serde_bytes")]
138 pub report_data: [u8; 64],
139}
140
141#[derive(Debug, Clone)]
143pub struct TDAttributes {
144 pub tud: u8,
147
148 pub sec: SECFlags,
150
151 pub other: OTHERFlags,
153}
154
155#[derive(Debug, Clone)]
157pub struct TUDFlags {
158 pub debug: bool,
161
162 pub reserved: u8,
164}
165
166#[derive(Debug, Clone)]
168pub struct SECFlags {
169 pub reserved_lower: u32,
171
172 pub sept_ve_disable: bool,
174
175 pub reserved_bit29: bool,
177
178 pub pks: bool,
180
181 pub kl: bool,
183}
184
185#[derive(Debug, Clone)]
187pub struct OTHERFlags {
188 pub reserved: u32,
190
191 pub perfmon: bool,
193}
194
195impl TDAttributes {
196 pub fn parse(input: [u8; 8]) -> Result<Self, scale::Error> {
197 let tud = input[0];
198 let reserved_lower =
200 (((input[3] & 0x0f) as u32) << 16) | ((input[2] as u32) << 8) | (input[1] as u32);
201 let sept_ve_disable = (input[3] & 0x10) != 0; let reserved_bit29 = (input[3] & 0x20) != 0; let pks = (input[3] & 0x40) != 0; let kl = (input[3] & 0x80) != 0; let reserved_other = (((input[7] as u32) & 0x7F) << 24)
209 | ((input[6] as u32) << 16)
210 | ((input[5] as u32) << 8)
211 | (input[4] as u32);
212 let perfmon = (input[7] & 0x80) != 0; Ok(TDAttributes {
215 tud,
216 sec: SECFlags {
217 reserved_lower,
218 sept_ve_disable,
219 reserved_bit29,
220 pks,
221 kl,
222 },
223 other: OTHERFlags {
224 reserved: reserved_other,
225 perfmon,
226 },
227 })
228 }
229}
230
231#[derive(
232 Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
233)]
234#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
235#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
236pub struct TDReport10 {
237 #[serde(with = "serde_bytes")]
238 pub tee_tcb_svn: [u8; 16],
239 #[serde(with = "serde_bytes")]
240 pub mr_seam: [u8; 48],
241 #[serde(with = "serde_bytes")]
242 pub mr_signer_seam: [u8; 48],
243 #[serde(with = "serde_bytes")]
244 pub seam_attributes: [u8; 8],
245 #[serde(with = "serde_bytes")]
246 pub td_attributes: [u8; 8],
247 #[serde(with = "serde_bytes")]
248 pub xfam: [u8; 8],
249 #[serde(with = "serde_bytes")]
250 pub mr_td: [u8; 48],
251 #[serde(with = "serde_bytes")]
252 pub mr_config_id: [u8; 48],
253 #[serde(with = "serde_bytes")]
254 pub mr_owner: [u8; 48],
255 #[serde(with = "serde_bytes")]
256 pub mr_owner_config: [u8; 48],
257 #[serde(with = "serde_bytes")]
258 pub rt_mr0: [u8; 48],
259 #[serde(with = "serde_bytes")]
260 pub rt_mr1: [u8; 48],
261 #[serde(with = "serde_bytes")]
262 pub rt_mr2: [u8; 48],
263 #[serde(with = "serde_bytes")]
264 pub rt_mr3: [u8; 48],
265 #[serde(with = "serde_bytes")]
266 pub report_data: [u8; 64],
267}
268
269#[derive(
270 Decode, Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
271)]
272#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
273#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
274pub struct TDReport15 {
275 pub base: TDReport10,
276 #[serde(with = "serde_bytes")]
277 pub tee_tcb_svn2: [u8; 16],
278 #[serde(with = "serde_bytes")]
279 pub mr_service_td: [u8; 48],
280}
281
282#[derive(Decode, Encode, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
283#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
284#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
285pub struct CertificationData {
286 pub cert_type: u16,
287 pub body: Data<u32>,
288}
289
290impl core::fmt::Debug for CertificationData {
291 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
292 let body_str = String::from_utf8_lossy(&self.body.data);
293 f.debug_struct("CertificationData")
294 .field("cert_type", &self.cert_type)
295 .field("body", &body_str)
296 .finish()
297 }
298}
299
300#[derive(
301 Decode, Encode, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
302)]
303#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
304#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
305pub struct QEReportCertificationData {
306 #[serde(with = "serde_bytes")]
307 pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
308 #[serde(with = "serde_bytes")]
309 pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
310 pub qe_auth_data: Data<u16>,
311 pub certification_data: CertificationData,
312}
313
314#[derive(
315 Decode, Encode, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize,
316)]
317#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
318#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
319pub struct AuthDataV3 {
320 #[serde(with = "serde_bytes")]
321 pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
322 #[serde(with = "serde_bytes")]
323 pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
324 #[serde(with = "serde_bytes")]
325 pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
326 #[serde(with = "serde_bytes")]
327 pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
328 pub qe_auth_data: Data<u16>,
329 pub certification_data: CertificationData,
330}
331
332#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
333#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
334#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
335pub struct AuthDataV4 {
336 #[serde(with = "serde_bytes")]
337 pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
338 #[serde(with = "serde_bytes")]
339 pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
340 pub certification_data: CertificationData,
341 pub qe_report_data: QEReportCertificationData,
342}
343
344impl AuthDataV4 {
345 pub fn into_v3(self) -> AuthDataV3 {
346 AuthDataV3 {
347 ecdsa_signature: self.ecdsa_signature,
348 ecdsa_attestation_key: self.ecdsa_attestation_key,
349 qe_report: self.qe_report_data.qe_report,
350 qe_report_signature: self.qe_report_data.qe_report_signature,
351 qe_auth_data: self.qe_report_data.qe_auth_data,
352 certification_data: self.qe_report_data.certification_data,
353 }
354 }
355}
356
357impl Decode for AuthDataV4 {
358 fn decode<I: Input>(input: &mut I) -> Result<Self, scale::Error> {
359 let ecdsa_signature = Decode::decode(input)?;
360 let ecdsa_attestation_key = Decode::decode(input)?;
361 let certification_data: CertificationData = Decode::decode(input)?;
362 let qe_report_data =
363 QEReportCertificationData::decode(&mut &certification_data.body.data[..])?;
364 Ok(AuthDataV4 {
365 ecdsa_signature,
366 ecdsa_attestation_key,
367 certification_data,
368 qe_report_data,
369 })
370 }
371}
372
373impl Encode for AuthDataV4 {
374 fn encode_to<O: Output + ?Sized>(&self, output: &mut O) {
375 self.ecdsa_signature.encode_to(output);
376 self.ecdsa_attestation_key.encode_to(output);
377
378 let mut qe_data_bytes = Vec::new();
380 self.qe_report_data.encode_to(&mut qe_data_bytes);
381
382 let cert_data = CertificationData {
383 cert_type: self.certification_data.cert_type,
384 body: Data {
385 data: qe_data_bytes,
386 _marker: core::marker::PhantomData,
387 },
388 };
389 cert_data.encode_to(output);
390 }
391}
392
393#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
394#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
395pub enum AuthData {
396 V3(AuthDataV3),
397 V4(AuthDataV4),
398}
399
400#[cfg(feature = "borsh_schema")]
403impl borsh::BorshSchema for AuthData {
404 fn declaration() -> borsh::schema::Declaration {
405 "AuthData".to_string()
406 }
407
408 fn add_definitions_recursively(
409 definitions: &mut borsh::__private::maybestd::collections::BTreeMap<
410 borsh::schema::Declaration,
411 borsh::schema::Definition,
412 >,
413 ) {
414 let definition = borsh::schema::Definition::Enum {
415 tag_width: 1,
416 variants: vec![
417 (0, "V3".to_string(), AuthDataV3::declaration()),
418 (1, "V4".to_string(), AuthDataV4::declaration()),
419 ],
420 };
421
422 borsh::schema::add_definition(Self::declaration(), definition, definitions);
423
424 AuthDataV3::add_definitions_recursively(definitions);
425 AuthDataV4::add_definitions_recursively(definitions);
426 }
427}
428
429impl AuthData {
430 pub fn into_v3(self) -> AuthDataV3 {
431 match self {
432 AuthData::V3(data) => data,
433 AuthData::V4(data) => data.into_v3(),
434 }
435 }
436}
437
438impl Encode for AuthData {
439 fn encode_to<O: Output + ?Sized>(&self, output: &mut O) {
440 match self {
441 AuthData::V3(data) => data.encode_to(output),
442 AuthData::V4(data) => data.encode_to(output),
443 }
444 }
445}
446
447fn decode_auth_data(ver: u16, input: &mut &[u8]) -> Result<AuthData, scale::Error> {
448 match ver {
449 3 => {
450 let auth_data = AuthDataV3::decode(input)?;
451 Ok(AuthData::V3(auth_data))
452 }
453 4 => {
454 let auth_data = AuthDataV4::decode(input)?;
455 Ok(AuthData::V4(auth_data))
456 }
457 _ => Err(scale::Error::from("Unsupported auth data version")),
458 }
459}
460
461#[derive(Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
462#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
463#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
464pub enum Report {
465 SgxEnclave(EnclaveReport),
466 TD10(TDReport10),
467 TD15(TDReport15),
468}
469
470impl Report {
471 pub fn is_sgx(&self) -> bool {
472 matches!(self, Report::SgxEnclave(_))
473 }
474
475 pub fn as_td10(&self) -> Option<&TDReport10> {
476 match self {
477 Report::TD10(report) => Some(report),
478 Report::TD15(report) => Some(&report.base),
479 _ => None,
480 }
481 }
482
483 pub fn as_td15(&self) -> Option<&TDReport15> {
484 match self {
485 Report::TD15(report) => Some(report),
486 _ => None,
487 }
488 }
489
490 pub fn as_sgx(&self) -> Option<&EnclaveReport> {
491 match self {
492 Report::SgxEnclave(report) => Some(report),
493 _ => None,
494 }
495 }
496}
497
498#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
499#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
500#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
501pub struct Quote {
502 pub header: Header,
503 pub report: Report,
504 pub auth_data: AuthData,
505}
506
507impl Decode for Quote {
508 fn decode<I: Input>(input: &mut I) -> Result<Self, scale::Error> {
509 let header = Header::decode(input)?;
510 let report;
511 match header.version {
512 3 => {
513 if header.tee_type != TEE_TYPE_SGX {
514 return Err(scale::Error::from("Invalid TEE type"));
515 }
516 report = Report::SgxEnclave(EnclaveReport::decode(input)?);
517 }
518 4 => match header.tee_type {
519 TEE_TYPE_SGX => {
520 report = Report::SgxEnclave(EnclaveReport::decode(input)?);
521 }
522 TEE_TYPE_TDX => {
523 report = Report::TD10(TDReport10::decode(input)?);
524 }
525 _ => return Err(scale::Error::from("Invalid TEE type")),
526 },
527 5 => {
528 let body = Body::decode(input)?;
529 match body.body_type {
530 BODY_SGX_ENCLAVE_REPORT_TYPE => {
531 report = Report::SgxEnclave(EnclaveReport::decode(input)?);
532 }
533 BODY_TD_REPORT10_TYPE => {
534 report = Report::TD10(TDReport10::decode(input)?);
535 }
536 BODY_TD_REPORT15_TYPE => {
537 report = Report::TD15(TDReport15::decode(input)?);
538 }
539 _ => return Err(scale::Error::from("Unsupported body type")),
540 }
541 }
542 _ => return Err(scale::Error::from("Unsupported quote version")),
543 }
544 let data = Data::<u32>::decode(input)?;
545 let auth_version = if header.version == 5 {
547 4
548 } else {
549 header.version
550 };
551 let auth_data = decode_auth_data(auth_version, &mut &data.data[..])?;
552 Ok(Quote {
553 header,
554 report,
555 auth_data,
556 })
557 }
558}
559
560impl Encode for Quote {
561 fn encode_to<O: Output + ?Sized>(&self, output: &mut O) {
562 self.header.encode_to(output);
564
565 if self.header.version == 5 {
567 let body = match &self.report {
568 Report::SgxEnclave(_) => Body {
569 body_type: BODY_SGX_ENCLAVE_REPORT_TYPE,
570 size: ENCLAVE_REPORT_BYTE_LEN as u32,
571 },
572 Report::TD10(_) => Body {
573 body_type: BODY_TD_REPORT10_TYPE,
574 size: TD_REPORT10_BYTE_LEN as u32,
575 },
576 Report::TD15(_) => Body {
577 body_type: BODY_TD_REPORT15_TYPE,
578 size: TD_REPORT15_BYTE_LEN as u32,
579 },
580 };
581 body.encode_to(output);
582 }
583
584 match &self.report {
586 Report::SgxEnclave(report) => report.encode_to(output),
587 Report::TD10(report) => report.encode_to(output),
588 Report::TD15(report) => report.encode_to(output),
589 }
590
591 let mut auth_data_bytes = Vec::new();
593 self.auth_data.encode_to(&mut auth_data_bytes);
594 let auth_data_len = auth_data_bytes.len() as u32;
595 auth_data_len.encode_to(output);
596 output.write(&auth_data_bytes);
597 }
598}
599
600impl Quote {
601 pub fn parse(quote: &[u8]) -> Result<Self> {
603 let mut input = quote;
604 let quote = Quote::decode(&mut input)?;
605 Ok(quote)
606 }
607
608 pub fn raw_cert_chain(&self) -> Result<&[u8]> {
610 let cert_data = match &self.auth_data {
611 AuthData::V3(data) => &data.certification_data,
612 AuthData::V4(data) => &data.qe_report_data.certification_data,
613 };
614 if cert_data.cert_type != 5 {
615 bail!("Unsupported cert type: {}", cert_data.cert_type);
616 }
617 Ok(&cert_data.body.data)
618 }
619
620 pub fn signed_length(&self) -> usize {
622 let mut len = match self.report {
623 Report::SgxEnclave(_) => HEADER_BYTE_LEN + ENCLAVE_REPORT_BYTE_LEN,
624 Report::TD10(_) => HEADER_BYTE_LEN + TD_REPORT10_BYTE_LEN,
625 Report::TD15(_) => HEADER_BYTE_LEN + TD_REPORT15_BYTE_LEN,
626 };
627 #[allow(clippy::arithmetic_side_effects)]
628 if self.header.version == 5 {
629 len += BODY_BYTE_SIZE;
630 }
631 len
632 }
633
634 pub fn inner_cert_type(&self) -> u16 {
638 match &self.auth_data {
639 AuthData::V3(data) => data.certification_data.cert_type,
640 AuthData::V4(data) => data.qe_report_data.certification_data.cert_type,
641 }
642 }
643
644 pub fn inner_cert_data(&self) -> &[u8] {
646 match &self.auth_data {
647 AuthData::V3(data) => &data.certification_data.body.data,
648 AuthData::V4(data) => &data.qe_report_data.certification_data.body.data,
649 }
650 }
651
652 pub fn qe_report(&self) -> &[u8; ENCLAVE_REPORT_BYTE_LEN] {
654 match &self.auth_data {
655 AuthData::V3(data) => &data.qe_report,
656 AuthData::V4(data) => &data.qe_report_data.qe_report,
657 }
658 }
659
660 pub fn qeid(&self) -> &[u8] {
662 &self.header.user_data[..16]
663 }
664
665 pub fn encrypted_ppid_params(&self) -> Result<EncryptedPpidParams> {
668 #[derive(Decode)]
675 struct EncPpidDecoder<const N: usize> {
676 encrypted_ppid: [u8; N],
677 cpusvn: CpuSvn,
678 pcesvn: Svn,
679 pceid: [u8; 2],
680 }
681 impl<const N: usize> EncPpidDecoder<N> {
682 fn into_params(self) -> EncryptedPpidParams {
683 EncryptedPpidParams {
684 encrypted_ppid: self.encrypted_ppid.to_vec(),
685 cpusvn: self.cpusvn,
686 pcesvn: self.pcesvn,
687 pceid: self.pceid,
688 }
689 }
690 }
691
692 let mut cert_body = self.inner_cert_data();
693 let params = match self.inner_cert_type() {
694 PCK_ID_ENCRYPTED_PPID_2048 => EncPpidDecoder::<256>::decode(&mut cert_body)
695 .context("Failed to decode ENCRYPTED_PPID_2048")?
696 .into_params(),
697 PCK_ID_ENCRYPTED_PPID_3072 => EncPpidDecoder::<384>::decode(&mut cert_body)
698 .context("Failed to decode ENCRYPTED_PPID_3072")?
699 .into_params(),
700 other => bail!("encrypted_ppid_params() requires cert_type 2 or 3, got {other}"),
701 };
702 Ok(params)
703 }
704}
705
706#[derive(Debug, Clone)]
709pub struct EncryptedPpidParams {
710 pub encrypted_ppid: Vec<u8>,
712 pub cpusvn: [u8; 16],
714 pub pcesvn: u16,
716 pub pceid: [u8; 2],
718}