opentitanlib/chip/
boot_svc.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::Result;
6use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use serde_annotate::Annotate;
8use sha2::{Digest, Sha256};
9use sphincsplus::SpxSecretKey;
10use std::convert::TryFrom;
11use std::io::{Read, Write};
12
13use super::ChipDataError;
14use crate::chip::boolean::HardenedBool;
15use crate::chip::rom_error::RomError;
16use crate::crypto::ecdsa::{EcdsaPrivateKey, EcdsaPublicKey, EcdsaRawPublicKey, EcdsaRawSignature};
17use crate::ownership::{DetachedSignature, OwnershipKeyAlg};
18use crate::with_unknown;
19
20with_unknown! {
21    pub enum BootSlot: u32 [default = Self::Unknown] {
22        Unknown = 0,
23        SlotA = u32::from_le_bytes(*b"AA__"),
24        SlotB = u32::from_le_bytes(*b"__BB"),
25        Unspecified = u32::from_le_bytes(*b"UUUU"),
26    }
27
28    /// The unlock mode for the OwnershipUnlock command.
29    pub enum UnlockMode: u32 [default = Self::Unknown] {
30        Unknown = 0,
31        /// Unlock the chip to accept any next owner.
32        Any = 0x00594e41,
33        /// Unlock the chip to accept only the endorsed next owner.
34        Endorsed = 0x4f444e45,
35        /// Unlock the chip to update the current owner configuration.
36        Update = 0x00445055,
37        /// Abort the unlock operation.
38        Abort = 0x54524241,
39    }
40
41    pub enum BootSvcKind: u32 [default = Self::Unknown] {
42        Unknown = 0,
43        EmptyRequest = u32::from_le_bytes(*b"EMPT"),
44        EmptyResponse = u32::from_le_bytes(*b"TPME"),
45        MinBl0SecVerRequest = u32::from_le_bytes(*b"MSEC"),
46        MinBl0SecVerResponse = u32::from_le_bytes(*b"CESM"),
47        NextBl0SlotRequest = u32::from_le_bytes(*b"NEXT"),
48        NextBl0SlotResponse = u32::from_le_bytes(*b"TXEN"),
49        OwnershipUnlockRequest = u32::from_le_bytes(*b"UNLK"),
50        OwnershipUnlockResponse = u32::from_le_bytes(*b"KLNU"),
51        OwnershipActivateRequest = u32::from_le_bytes(*b"ACTV"),
52        OwnershipActivateResponse = u32::from_le_bytes(*b"VTCA"),
53    }
54}
55
56impl BootSlot {
57    pub fn opposite(self) -> Result<Self> {
58        match self {
59            BootSlot::SlotA => Ok(BootSlot::SlotB),
60            BootSlot::SlotB => Ok(BootSlot::SlotA),
61            _ => Err(ChipDataError::BadSlot(self).into()),
62        }
63    }
64}
65
66/// The Boot Services header common to all boot services commands and responses.
67#[derive(Debug, Default, Annotate)]
68pub struct Header {
69    /// A SHA256 digest over the rest of the boot services message.
70    #[annotate(format=hex)]
71    pub digest: [u32; 8],
72    /// A tag that identifies this struct as a boot services message ('BSVC').
73    #[annotate(format=hex)]
74    pub identifier: u32,
75    /// The type of boot services message that follows this header.
76    pub kind: BootSvcKind,
77    /// The length of the boot services message in bytes (including the header).
78    pub length: u32,
79}
80
81/// An empty boot services message.
82#[derive(Debug, Default, Annotate)]
83pub struct Empty {
84    #[annotate(format=hex)]
85    pub payload: Vec<u32>,
86}
87
88/// Request to set the minimum owner stage firmware version.
89#[derive(Debug, Default, Annotate)]
90pub struct MinBl0SecVerRequest {
91    /// The desired minimum BL0 version.
92    pub ver: u32,
93}
94
95/// Response to the minimum version request.
96#[derive(Debug, Default, Annotate)]
97pub struct MinBl0SecVerResponse {
98    /// The current minimum BL0 version.
99    pub ver: u32,
100    /// The status response to the request.
101    #[annotate(format = hex)]
102    pub status: RomError,
103}
104
105/// Request to set the next (one-time) owner stage boot slot.
106#[derive(Debug, Default, Annotate)]
107pub struct NextBl0SlotRequest {
108    /// The slot to boot.
109    pub next_bl0_slot: BootSlot,
110    /// The slot to configure as primary.
111    pub primary_bl0_slot: BootSlot,
112}
113
114/// Response to the set next boot slot request.
115#[derive(Debug, Default, Annotate)]
116pub struct NextBl0SlotResponse {
117    /// The status response to the request.
118    #[annotate(format = hex)]
119    pub status: RomError,
120    /// The current primary slot.
121    pub primary_bl0_slot: BootSlot,
122}
123
124/// Request to unlock ownership of the chip.
125#[derive(Debug, Default, Annotate)]
126pub struct OwnershipUnlockRequest {
127    /// The desired unlock mode.
128    pub unlock_mode: UnlockMode,
129    /// The Device Identification Number of the chip.
130    pub din: u64,
131    /// Reserved for future use.
132    #[serde(with = "serde_bytes", skip_serializing_if = "Vec::is_empty")]
133    #[annotate(format=hexstr)]
134    pub reserved: Vec<u8>,
135    /// The ROM_EXT nonce.
136    #[annotate(format=hex)]
137    pub nonce: u64,
138    /// The algorithm of next owner's key (for unlock Endorsed mode).
139    pub next_owner_alg: OwnershipKeyAlg,
140    /// The next owner's key (for unlock Endorsed mode).
141    pub next_owner_key: EcdsaRawPublicKey,
142    // TODO(cfrantz): Hybrid key material
143    #[serde(with = "serde_bytes", skip_serializing_if = "Vec::is_empty")]
144    #[annotate(format=hexstr)]
145    pub hybrid_padding: Vec<u8>,
146    /// A signature over [unlock_mode..next_owner_key] with the current owner unlock key.
147    pub signature: EcdsaRawSignature,
148}
149
150/// Response to the ownership unlock command.
151#[derive(Debug, Default, Annotate)]
152pub struct OwnershipUnlockResponse {
153    /// The status response to the request.
154    #[annotate(format = hex)]
155    pub status: RomError,
156}
157
158/// Request to activate ownership of the chip.
159#[derive(Debug, Default, Annotate)]
160pub struct OwnershipActivateRequest {
161    /// The new primary boot slot after activating ownership.
162    pub primary_bl0_slot: BootSlot,
163    /// The Device Identification Number of the chip.
164    pub din: u64,
165    /// Whether to erase the previous owner's data during activation.
166    pub erase_previous: HardenedBool,
167    /// Reserved for future use.
168    #[serde(with = "serde_bytes", skip_serializing_if = "Vec::is_empty")]
169    #[annotate(format=hexstr)]
170    pub reserved: Vec<u8>,
171    /// The ROM_EXT nonce.
172    #[annotate(format=hex)]
173    pub nonce: u64,
174    /// A signature over [primary_bl0_slot..nonce] with the next owner's activate key.
175    pub signature: EcdsaRawSignature,
176}
177
178/// Response to the ownership activate command.
179#[derive(Debug, Default, Annotate)]
180pub struct OwnershipActivateResponse {
181    /// The status response to the request.
182    #[annotate(format = hex)]
183    pub status: RomError,
184}
185
186#[derive(Debug, Annotate)]
187pub enum Message {
188    Raw(
189        #[serde(with = "serde_bytes")]
190        #[annotate(format=hexdump)]
191        Vec<u8>,
192    ),
193    Empty(Empty),
194    MinBl0SecVerRequest(MinBl0SecVerRequest),
195    NextBl0SlotRequest(NextBl0SlotRequest),
196    OwnershipUnlockRequest(OwnershipUnlockRequest),
197    OwnershipActivateRequest(OwnershipActivateRequest),
198    MinBl0SecVerResponse(MinBl0SecVerResponse),
199    NextBl0SlotResponse(NextBl0SlotResponse),
200    OwnershipUnlockResponse(OwnershipUnlockResponse),
201    OwnershipActivateResponse(OwnershipActivateResponse),
202}
203
204#[derive(Debug, Annotate)]
205pub struct BootSvc {
206    pub header: Header,
207    pub message: Message,
208}
209
210impl TryFrom<&[u8]> for BootSvc {
211    type Error = ChipDataError;
212    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
213        let header = Header::try_from(buf)?;
214        let len = header.length as usize;
215        if buf.len() - Header::SIZE < len {
216            return Err(ChipDataError::BadSize(len, buf.len()));
217        }
218        let mut digest = Sha256::digest(&buf[Header::HASH_LEN..Header::SIZE]);
219        digest.reverse();
220        if digest[..] == buf[..Header::HASH_LEN] {
221            return Err(ChipDataError::InvalidDigest);
222        }
223        let buf = &buf[Header::SIZE..];
224        let message = match header.kind {
225            BootSvcKind::EmptyRequest => Message::Empty(TryFrom::try_from(buf)?),
226            BootSvcKind::EmptyResponse => Message::Empty(TryFrom::try_from(buf)?),
227            BootSvcKind::MinBl0SecVerRequest => {
228                Message::MinBl0SecVerRequest(TryFrom::try_from(buf)?)
229            }
230            BootSvcKind::MinBl0SecVerResponse => {
231                Message::MinBl0SecVerResponse(TryFrom::try_from(buf)?)
232            }
233            BootSvcKind::NextBl0SlotRequest => Message::NextBl0SlotRequest(TryFrom::try_from(buf)?),
234            BootSvcKind::NextBl0SlotResponse => {
235                Message::NextBl0SlotResponse(TryFrom::try_from(buf)?)
236            }
237            BootSvcKind::OwnershipUnlockRequest => {
238                Message::OwnershipUnlockRequest(TryFrom::try_from(buf)?)
239            }
240            BootSvcKind::OwnershipUnlockResponse => {
241                Message::OwnershipUnlockResponse(TryFrom::try_from(buf)?)
242            }
243            BootSvcKind::OwnershipActivateRequest => {
244                Message::OwnershipActivateRequest(TryFrom::try_from(buf)?)
245            }
246            BootSvcKind::OwnershipActivateResponse => {
247                Message::OwnershipActivateResponse(TryFrom::try_from(buf)?)
248            }
249            _ => Message::Raw(buf.to_vec()),
250        };
251
252        Ok(BootSvc { header, message })
253    }
254}
255
256impl BootSvc {
257    pub fn to_bytes(&self) -> Result<Vec<u8>> {
258        let mut data = Vec::new();
259        self.header.write(&mut data)?;
260        match &self.message {
261            Message::Empty(m) => m.write(&mut data)?,
262            Message::MinBl0SecVerRequest(m) => m.write(&mut data)?,
263            Message::MinBl0SecVerResponse(m) => m.write(&mut data)?,
264            Message::NextBl0SlotRequest(m) => m.write(&mut data)?,
265            Message::NextBl0SlotResponse(m) => m.write(&mut data)?,
266            Message::OwnershipUnlockRequest(m) => m.write(&mut data)?,
267            Message::OwnershipUnlockResponse(m) => m.write(&mut data)?,
268            Message::OwnershipActivateRequest(m) => m.write(&mut data)?,
269            Message::OwnershipActivateResponse(m) => m.write(&mut data)?,
270            Message::Raw(m) => data.extend_from_slice(m.as_slice()),
271        };
272        let mut digest = Sha256::digest(&data[Header::HASH_LEN..]);
273        digest.reverse();
274        data[..Header::HASH_LEN].copy_from_slice(&digest);
275        Ok(data)
276    }
277
278    pub fn empty(payload: &[u32]) -> Self {
279        BootSvc {
280            header: Header {
281                digest: [0u32; 8],
282                identifier: Header::IDENTIFIER,
283                kind: BootSvcKind::EmptyRequest,
284                length: (Header::SIZE + Empty::SIZE) as u32,
285            },
286            message: Message::Empty(Empty {
287                payload: payload.to_vec(),
288            }),
289        }
290    }
291
292    pub fn min_bl0_sec_ver(ver: u32) -> Self {
293        BootSvc {
294            header: Header {
295                digest: [0u32; 8],
296                identifier: Header::IDENTIFIER,
297                kind: BootSvcKind::MinBl0SecVerRequest,
298                length: (Header::SIZE + MinBl0SecVerRequest::SIZE) as u32,
299            },
300            message: Message::MinBl0SecVerRequest(MinBl0SecVerRequest { ver }),
301        }
302    }
303
304    pub fn next_boot_bl0_slot(primary: BootSlot, next: BootSlot) -> Self {
305        BootSvc {
306            header: Header {
307                digest: [0u32; 8],
308                identifier: Header::IDENTIFIER,
309                kind: BootSvcKind::NextBl0SlotRequest,
310                length: (Header::SIZE + NextBl0SlotRequest::SIZE) as u32,
311            },
312            message: Message::NextBl0SlotRequest(NextBl0SlotRequest {
313                next_bl0_slot: next,
314                primary_bl0_slot: primary,
315            }),
316        }
317    }
318
319    pub fn ownership_unlock(unlock: OwnershipUnlockRequest) -> Self {
320        BootSvc {
321            header: Header {
322                digest: [0u32; 8],
323                identifier: Header::IDENTIFIER,
324                kind: BootSvcKind::OwnershipUnlockRequest,
325                length: (Header::SIZE + OwnershipUnlockRequest::SIZE) as u32,
326            },
327            message: Message::OwnershipUnlockRequest(unlock),
328        }
329    }
330
331    pub fn ownership_activate(activate: OwnershipActivateRequest) -> Self {
332        BootSvc {
333            header: Header {
334                digest: [0u32; 8],
335                identifier: Header::IDENTIFIER,
336                kind: BootSvcKind::OwnershipActivateRequest,
337                length: (Header::SIZE + OwnershipActivateRequest::SIZE) as u32,
338            },
339            message: Message::OwnershipActivateRequest(activate),
340        }
341    }
342}
343
344impl TryFrom<&[u8]> for Header {
345    type Error = ChipDataError;
346    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
347        let mut reader = std::io::Cursor::new(buf);
348        let mut val = Header::default();
349        reader.read_u32_into::<LittleEndian>(&mut val.digest)?;
350        val.identifier = reader.read_u32::<LittleEndian>()?;
351        val.kind = BootSvcKind(reader.read_u32::<LittleEndian>()?);
352        val.length = reader.read_u32::<LittleEndian>()?;
353        Ok(val)
354    }
355}
356impl Header {
357    pub const SIZE: usize = 44;
358    pub const IDENTIFIER: u32 = 0x43565342;
359    const HASH_LEN: usize = 32;
360
361    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
362        for d in self.digest.iter() {
363            dest.write_u32::<LittleEndian>(*d)?;
364        }
365        dest.write_u32::<LittleEndian>(self.identifier)?;
366        dest.write_u32::<LittleEndian>(u32::from(self.kind))?;
367        dest.write_u32::<LittleEndian>(self.length)?;
368        Ok(())
369    }
370}
371
372impl TryFrom<&[u8]> for Empty {
373    type Error = ChipDataError;
374    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
375        let mut reader = std::io::Cursor::new(buf);
376        let mut val = Empty::default();
377        val.payload.resize(64, 0);
378        reader.read_u32_into::<LittleEndian>(&mut val.payload)?;
379        Ok(val)
380    }
381}
382impl Empty {
383    pub const SIZE: usize = 256;
384    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
385        for i in 0..64 {
386            let p = self.payload.get(i).unwrap_or(&0);
387            dest.write_u32::<LittleEndian>(*p)?;
388        }
389        Ok(())
390    }
391}
392
393impl TryFrom<&[u8]> for MinBl0SecVerRequest {
394    type Error = ChipDataError;
395    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
396        let mut reader = std::io::Cursor::new(buf);
397        Ok(MinBl0SecVerRequest {
398            ver: reader.read_u32::<LittleEndian>()?,
399        })
400    }
401}
402impl MinBl0SecVerRequest {
403    pub const SIZE: usize = 4;
404    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
405        dest.write_u32::<LittleEndian>(self.ver)?;
406        Ok(())
407    }
408}
409
410impl TryFrom<&[u8]> for MinBl0SecVerResponse {
411    type Error = ChipDataError;
412    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
413        let mut reader = std::io::Cursor::new(buf);
414        Ok(MinBl0SecVerResponse {
415            ver: reader.read_u32::<LittleEndian>()?,
416            status: RomError(reader.read_u32::<LittleEndian>()?),
417        })
418    }
419}
420impl MinBl0SecVerResponse {
421    pub const SIZE: usize = 8;
422    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
423        dest.write_u32::<LittleEndian>(self.ver)?;
424        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
425        Ok(())
426    }
427}
428
429impl TryFrom<&[u8]> for NextBl0SlotRequest {
430    type Error = ChipDataError;
431    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
432        let mut reader = std::io::Cursor::new(buf);
433        Ok(NextBl0SlotRequest {
434            next_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
435            primary_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
436        })
437    }
438}
439impl NextBl0SlotRequest {
440    pub const SIZE: usize = 8;
441    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
442        dest.write_u32::<LittleEndian>(u32::from(self.next_bl0_slot))?;
443        dest.write_u32::<LittleEndian>(u32::from(self.primary_bl0_slot))?;
444        Ok(())
445    }
446}
447
448impl TryFrom<&[u8]> for NextBl0SlotResponse {
449    type Error = ChipDataError;
450    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
451        let mut reader = std::io::Cursor::new(buf);
452        Ok(NextBl0SlotResponse {
453            status: RomError(reader.read_u32::<LittleEndian>()?),
454            primary_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
455        })
456    }
457}
458impl NextBl0SlotResponse {
459    pub const SIZE: usize = 4;
460    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
461        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
462        Ok(())
463    }
464}
465
466impl TryFrom<&[u8]> for OwnershipUnlockRequest {
467    type Error = ChipDataError;
468    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
469        let mut reader = std::io::Cursor::new(buf);
470        let mut val = Self::default();
471        val.unlock_mode = UnlockMode(reader.read_u32::<LittleEndian>()?);
472        val.din = reader.read_u64::<LittleEndian>()?;
473        val.reserved.resize(Self::RESERVED_SIZE, 0);
474        reader.read_exact(&mut val.reserved)?;
475        val.next_owner_alg = OwnershipKeyAlg(reader.read_u32::<LittleEndian>()?);
476        val.nonce = reader.read_u64::<LittleEndian>()?;
477        val.next_owner_key = EcdsaRawPublicKey::read(&mut reader).map_err(ChipDataError::Anyhow)?;
478
479        // SPX+ public key size is 32 bytes.
480        val.hybrid_padding.resize(32, 0);
481        reader.read_exact(&mut val.hybrid_padding)?;
482
483        val.signature = EcdsaRawSignature::read(&mut reader).map_err(ChipDataError::Anyhow)?;
484        Ok(val)
485    }
486}
487impl OwnershipUnlockRequest {
488    pub const SIZE: usize = 212;
489    const RESERVED_SIZE: usize = 7 * std::mem::size_of::<u32>();
490    const SIGNATURE_OFFSET: usize = 148;
491    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
492        dest.write_u32::<LittleEndian>(u32::from(self.unlock_mode))?;
493        dest.write_u64::<LittleEndian>(self.din)?;
494        for i in 0..Self::RESERVED_SIZE {
495            let p = self.reserved.get(i).unwrap_or(&0x00);
496            dest.write_all(std::slice::from_ref(p))?;
497        }
498        dest.write_u32::<LittleEndian>(u32::from(self.next_owner_alg))?;
499        dest.write_u64::<LittleEndian>(self.nonce)?;
500        self.next_owner_key.write(dest)?;
501
502        // SPX+ public key size is 32 bytes.
503        for i in 0..32 {
504            let p = self.hybrid_padding.get(i).unwrap_or(&0x00);
505            dest.write_all(std::slice::from_ref(p))?;
506        }
507
508        self.signature.write(dest)?;
509        Ok(())
510    }
511
512    pub fn set_next_owner_key(&mut self, key: &EcdsaPublicKey) -> Result<()> {
513        self.next_owner_key = EcdsaRawPublicKey::try_from(key)?;
514        Ok(())
515    }
516
517    pub fn sign(&mut self, key: &EcdsaPrivateKey) -> Result<()> {
518        let mut data = Vec::new();
519        self.write(&mut data)?;
520        self.signature = key.digest_and_sign(&data[..Self::SIGNATURE_OFFSET])?;
521        Ok(())
522    }
523
524    pub fn detached_sign(
525        &mut self,
526        algorithm: OwnershipKeyAlg,
527        ecdsa_key: Option<&EcdsaPrivateKey>,
528        spx_key: Option<&SpxSecretKey>,
529    ) -> Result<DetachedSignature> {
530        self.signature = Default::default();
531        let mut data = Vec::new();
532        self.write(&mut data)?;
533        DetachedSignature::new(
534            &data[..Self::SIGNATURE_OFFSET],
535            BootSvcKind::OwnershipUnlockRequest.into(),
536            algorithm,
537            self.nonce,
538            ecdsa_key,
539            spx_key,
540        )
541    }
542}
543
544impl TryFrom<&[u8]> for OwnershipUnlockResponse {
545    type Error = ChipDataError;
546    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
547        let mut reader = std::io::Cursor::new(buf);
548        Ok(OwnershipUnlockResponse {
549            status: RomError(reader.read_u32::<LittleEndian>()?),
550        })
551    }
552}
553impl OwnershipUnlockResponse {
554    pub const SIZE: usize = 4;
555    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
556        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
557        Ok(())
558    }
559}
560
561impl TryFrom<&[u8]> for OwnershipActivateRequest {
562    type Error = ChipDataError;
563    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
564        let mut reader = std::io::Cursor::new(buf);
565        let mut val = Self::default();
566        val.primary_bl0_slot = BootSlot(reader.read_u32::<LittleEndian>()?);
567        val.din = reader.read_u64::<LittleEndian>()?;
568        val.erase_previous = HardenedBool(reader.read_u32::<LittleEndian>()?);
569        val.reserved.resize(Self::RESERVED_SIZE, 0);
570        reader.read_exact(&mut val.reserved)?;
571        val.nonce = reader.read_u64::<LittleEndian>()?;
572        val.signature = EcdsaRawSignature::read(&mut reader).map_err(ChipDataError::Anyhow)?;
573        Ok(val)
574    }
575}
576impl OwnershipActivateRequest {
577    pub const SIZE: usize = 212;
578    const RESERVED_SIZE: usize = 31 * std::mem::size_of::<u32>();
579    const SIGNATURE_OFFSET: usize = 148;
580    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
581        dest.write_u32::<LittleEndian>(u32::from(self.primary_bl0_slot))?;
582        dest.write_u64::<LittleEndian>(self.din)?;
583        dest.write_u32::<LittleEndian>(u32::from(self.erase_previous))?;
584        for i in 0..Self::RESERVED_SIZE {
585            let p = self.reserved.get(i).unwrap_or(&0x00);
586            dest.write_all(std::slice::from_ref(p))?;
587        }
588        dest.write_u64::<LittleEndian>(self.nonce)?;
589        self.signature.write(dest)?;
590        Ok(())
591    }
592
593    pub fn sign(&mut self, key: &EcdsaPrivateKey) -> Result<()> {
594        let mut data = Vec::new();
595        self.write(&mut data)?;
596        self.signature = key.digest_and_sign(&data[..Self::SIGNATURE_OFFSET])?;
597        Ok(())
598    }
599
600    pub fn detached_sign(
601        &mut self,
602        algorithm: OwnershipKeyAlg,
603        ecdsa_key: Option<&EcdsaPrivateKey>,
604        spx_key: Option<&SpxSecretKey>,
605    ) -> Result<DetachedSignature> {
606        self.signature = Default::default();
607        let mut data = Vec::new();
608        self.write(&mut data)?;
609        DetachedSignature::new(
610            &data[..Self::SIGNATURE_OFFSET],
611            BootSvcKind::OwnershipActivateRequest.into(),
612            algorithm,
613            self.nonce,
614            ecdsa_key,
615            spx_key,
616        )
617    }
618}
619
620impl TryFrom<&[u8]> for OwnershipActivateResponse {
621    type Error = ChipDataError;
622    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
623        let mut reader = std::io::Cursor::new(buf);
624        Ok(OwnershipActivateResponse {
625            status: RomError(reader.read_u32::<LittleEndian>()?),
626        })
627    }
628}
629impl OwnershipActivateResponse {
630    pub const SIZE: usize = 4;
631    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
632        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
633        Ok(())
634    }
635}