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 next owner's key (for unlock Endorsed mode).
139    pub next_owner_key: EcdsaRawPublicKey,
140    // TODO(cfrantz): Hybrid key material
141    #[serde(with = "serde_bytes", skip_serializing_if = "Vec::is_empty")]
142    #[annotate(format=hexstr)]
143    pub hybrid_padding: Vec<u8>,
144    /// A signature over [unlock_mode..next_owner_key] with the current owner unlock key.
145    pub signature: EcdsaRawSignature,
146}
147
148/// Response to the ownership unlock command.
149#[derive(Debug, Default, Annotate)]
150pub struct OwnershipUnlockResponse {
151    /// The status response to the request.
152    #[annotate(format = hex)]
153    pub status: RomError,
154}
155
156/// Request to activate ownership of the chip.
157#[derive(Debug, Default, Annotate)]
158pub struct OwnershipActivateRequest {
159    /// The new primary boot slot after activating ownership.
160    pub primary_bl0_slot: BootSlot,
161    /// The Device Identification Number of the chip.
162    pub din: u64,
163    /// Whether to erase the previous owner's data during activation.
164    pub erase_previous: HardenedBool,
165    /// Reserved for future use.
166    #[serde(with = "serde_bytes", skip_serializing_if = "Vec::is_empty")]
167    #[annotate(format=hexstr)]
168    pub reserved: Vec<u8>,
169    /// The ROM_EXT nonce.
170    #[annotate(format=hex)]
171    pub nonce: u64,
172    /// A signature over [primary_bl0_slot..nonce] with the next owner's activate key.
173    pub signature: EcdsaRawSignature,
174}
175
176/// Response to the ownership activate command.
177#[derive(Debug, Default, Annotate)]
178pub struct OwnershipActivateResponse {
179    /// The status response to the request.
180    #[annotate(format = hex)]
181    pub status: RomError,
182}
183
184#[derive(Debug, Annotate)]
185pub enum Message {
186    Raw(
187        #[serde(with = "serde_bytes")]
188        #[annotate(format=hexdump)]
189        Vec<u8>,
190    ),
191    Empty(Empty),
192    MinBl0SecVerRequest(MinBl0SecVerRequest),
193    NextBl0SlotRequest(NextBl0SlotRequest),
194    OwnershipUnlockRequest(OwnershipUnlockRequest),
195    OwnershipActivateRequest(OwnershipActivateRequest),
196    MinBl0SecVerResponse(MinBl0SecVerResponse),
197    NextBl0SlotResponse(NextBl0SlotResponse),
198    OwnershipUnlockResponse(OwnershipUnlockResponse),
199    OwnershipActivateResponse(OwnershipActivateResponse),
200}
201
202#[derive(Debug, Annotate)]
203pub struct BootSvc {
204    pub header: Header,
205    pub message: Message,
206}
207
208impl TryFrom<&[u8]> for BootSvc {
209    type Error = ChipDataError;
210    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
211        let header = Header::try_from(buf)?;
212        let len = header.length as usize;
213        if buf.len() - Header::SIZE < len {
214            return Err(ChipDataError::BadSize(len, buf.len()));
215        }
216        let mut digest = Sha256::digest(&buf[Header::HASH_LEN..Header::SIZE]);
217        digest.reverse();
218        if digest[..] == buf[..Header::HASH_LEN] {
219            return Err(ChipDataError::InvalidDigest);
220        }
221        let buf = &buf[Header::SIZE..];
222        let message = match header.kind {
223            BootSvcKind::EmptyRequest => Message::Empty(TryFrom::try_from(buf)?),
224            BootSvcKind::EmptyResponse => Message::Empty(TryFrom::try_from(buf)?),
225            BootSvcKind::MinBl0SecVerRequest => {
226                Message::MinBl0SecVerRequest(TryFrom::try_from(buf)?)
227            }
228            BootSvcKind::MinBl0SecVerResponse => {
229                Message::MinBl0SecVerResponse(TryFrom::try_from(buf)?)
230            }
231            BootSvcKind::NextBl0SlotRequest => Message::NextBl0SlotRequest(TryFrom::try_from(buf)?),
232            BootSvcKind::NextBl0SlotResponse => {
233                Message::NextBl0SlotResponse(TryFrom::try_from(buf)?)
234            }
235            BootSvcKind::OwnershipUnlockRequest => {
236                Message::OwnershipUnlockRequest(TryFrom::try_from(buf)?)
237            }
238            BootSvcKind::OwnershipUnlockResponse => {
239                Message::OwnershipUnlockResponse(TryFrom::try_from(buf)?)
240            }
241            BootSvcKind::OwnershipActivateRequest => {
242                Message::OwnershipActivateRequest(TryFrom::try_from(buf)?)
243            }
244            BootSvcKind::OwnershipActivateResponse => {
245                Message::OwnershipActivateResponse(TryFrom::try_from(buf)?)
246            }
247            _ => Message::Raw(buf.to_vec()),
248        };
249
250        Ok(BootSvc { header, message })
251    }
252}
253
254impl BootSvc {
255    pub fn to_bytes(&self) -> Result<Vec<u8>> {
256        let mut data = Vec::new();
257        self.header.write(&mut data)?;
258        match &self.message {
259            Message::Empty(m) => m.write(&mut data)?,
260            Message::MinBl0SecVerRequest(m) => m.write(&mut data)?,
261            Message::MinBl0SecVerResponse(m) => m.write(&mut data)?,
262            Message::NextBl0SlotRequest(m) => m.write(&mut data)?,
263            Message::NextBl0SlotResponse(m) => m.write(&mut data)?,
264            Message::OwnershipUnlockRequest(m) => m.write(&mut data)?,
265            Message::OwnershipUnlockResponse(m) => m.write(&mut data)?,
266            Message::OwnershipActivateRequest(m) => m.write(&mut data)?,
267            Message::OwnershipActivateResponse(m) => m.write(&mut data)?,
268            Message::Raw(m) => data.extend_from_slice(m.as_slice()),
269        };
270        let mut digest = Sha256::digest(&data[Header::HASH_LEN..]);
271        digest.reverse();
272        data[..Header::HASH_LEN].copy_from_slice(&digest);
273        Ok(data)
274    }
275
276    pub fn empty(payload: &[u32]) -> Self {
277        BootSvc {
278            header: Header {
279                digest: [0u32; 8],
280                identifier: Header::IDENTIFIER,
281                kind: BootSvcKind::EmptyRequest,
282                length: (Header::SIZE + Empty::SIZE) as u32,
283            },
284            message: Message::Empty(Empty {
285                payload: payload.to_vec(),
286            }),
287        }
288    }
289
290    pub fn min_bl0_sec_ver(ver: u32) -> Self {
291        BootSvc {
292            header: Header {
293                digest: [0u32; 8],
294                identifier: Header::IDENTIFIER,
295                kind: BootSvcKind::MinBl0SecVerRequest,
296                length: (Header::SIZE + MinBl0SecVerRequest::SIZE) as u32,
297            },
298            message: Message::MinBl0SecVerRequest(MinBl0SecVerRequest { ver }),
299        }
300    }
301
302    pub fn next_boot_bl0_slot(primary: BootSlot, next: BootSlot) -> Self {
303        BootSvc {
304            header: Header {
305                digest: [0u32; 8],
306                identifier: Header::IDENTIFIER,
307                kind: BootSvcKind::NextBl0SlotRequest,
308                length: (Header::SIZE + NextBl0SlotRequest::SIZE) as u32,
309            },
310            message: Message::NextBl0SlotRequest(NextBl0SlotRequest {
311                next_bl0_slot: next,
312                primary_bl0_slot: primary,
313            }),
314        }
315    }
316
317    pub fn ownership_unlock(unlock: OwnershipUnlockRequest) -> Self {
318        BootSvc {
319            header: Header {
320                digest: [0u32; 8],
321                identifier: Header::IDENTIFIER,
322                kind: BootSvcKind::OwnershipUnlockRequest,
323                length: (Header::SIZE + OwnershipUnlockRequest::SIZE) as u32,
324            },
325            message: Message::OwnershipUnlockRequest(unlock),
326        }
327    }
328
329    pub fn ownership_activate(activate: OwnershipActivateRequest) -> Self {
330        BootSvc {
331            header: Header {
332                digest: [0u32; 8],
333                identifier: Header::IDENTIFIER,
334                kind: BootSvcKind::OwnershipActivateRequest,
335                length: (Header::SIZE + OwnershipActivateRequest::SIZE) as u32,
336            },
337            message: Message::OwnershipActivateRequest(activate),
338        }
339    }
340}
341
342impl TryFrom<&[u8]> for Header {
343    type Error = ChipDataError;
344    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
345        let mut reader = std::io::Cursor::new(buf);
346        let mut val = Header::default();
347        reader.read_u32_into::<LittleEndian>(&mut val.digest)?;
348        val.identifier = reader.read_u32::<LittleEndian>()?;
349        val.kind = BootSvcKind(reader.read_u32::<LittleEndian>()?);
350        val.length = reader.read_u32::<LittleEndian>()?;
351        Ok(val)
352    }
353}
354impl Header {
355    pub const SIZE: usize = 44;
356    pub const IDENTIFIER: u32 = 0x43565342;
357    const HASH_LEN: usize = 32;
358
359    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
360        for d in self.digest.iter() {
361            dest.write_u32::<LittleEndian>(*d)?;
362        }
363        dest.write_u32::<LittleEndian>(self.identifier)?;
364        dest.write_u32::<LittleEndian>(u32::from(self.kind))?;
365        dest.write_u32::<LittleEndian>(self.length)?;
366        Ok(())
367    }
368}
369
370impl TryFrom<&[u8]> for Empty {
371    type Error = ChipDataError;
372    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
373        let mut reader = std::io::Cursor::new(buf);
374        let mut val = Empty::default();
375        val.payload.resize(64, 0);
376        reader.read_u32_into::<LittleEndian>(&mut val.payload)?;
377        Ok(val)
378    }
379}
380impl Empty {
381    pub const SIZE: usize = 256;
382    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
383        for i in 0..64 {
384            let p = self.payload.get(i).unwrap_or(&0);
385            dest.write_u32::<LittleEndian>(*p)?;
386        }
387        Ok(())
388    }
389}
390
391impl TryFrom<&[u8]> for MinBl0SecVerRequest {
392    type Error = ChipDataError;
393    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
394        let mut reader = std::io::Cursor::new(buf);
395        Ok(MinBl0SecVerRequest {
396            ver: reader.read_u32::<LittleEndian>()?,
397        })
398    }
399}
400impl MinBl0SecVerRequest {
401    pub const SIZE: usize = 4;
402    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
403        dest.write_u32::<LittleEndian>(self.ver)?;
404        Ok(())
405    }
406}
407
408impl TryFrom<&[u8]> for MinBl0SecVerResponse {
409    type Error = ChipDataError;
410    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
411        let mut reader = std::io::Cursor::new(buf);
412        Ok(MinBl0SecVerResponse {
413            ver: reader.read_u32::<LittleEndian>()?,
414            status: RomError(reader.read_u32::<LittleEndian>()?),
415        })
416    }
417}
418impl MinBl0SecVerResponse {
419    pub const SIZE: usize = 8;
420    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
421        dest.write_u32::<LittleEndian>(self.ver)?;
422        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
423        Ok(())
424    }
425}
426
427impl TryFrom<&[u8]> for NextBl0SlotRequest {
428    type Error = ChipDataError;
429    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
430        let mut reader = std::io::Cursor::new(buf);
431        Ok(NextBl0SlotRequest {
432            next_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
433            primary_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
434        })
435    }
436}
437impl NextBl0SlotRequest {
438    pub const SIZE: usize = 8;
439    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
440        dest.write_u32::<LittleEndian>(u32::from(self.next_bl0_slot))?;
441        dest.write_u32::<LittleEndian>(u32::from(self.primary_bl0_slot))?;
442        Ok(())
443    }
444}
445
446impl TryFrom<&[u8]> for NextBl0SlotResponse {
447    type Error = ChipDataError;
448    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
449        let mut reader = std::io::Cursor::new(buf);
450        Ok(NextBl0SlotResponse {
451            status: RomError(reader.read_u32::<LittleEndian>()?),
452            primary_bl0_slot: BootSlot(reader.read_u32::<LittleEndian>()?),
453        })
454    }
455}
456impl NextBl0SlotResponse {
457    pub const SIZE: usize = 4;
458    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
459        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
460        Ok(())
461    }
462}
463
464impl TryFrom<&[u8]> for OwnershipUnlockRequest {
465    type Error = ChipDataError;
466    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
467        let mut reader = std::io::Cursor::new(buf);
468        let mut val = Self::default();
469        val.unlock_mode = UnlockMode(reader.read_u32::<LittleEndian>()?);
470        val.din = reader.read_u64::<LittleEndian>()?;
471        val.reserved.resize(Self::RESERVED_SIZE, 0);
472        reader.read_exact(&mut val.reserved)?;
473        val.nonce = reader.read_u64::<LittleEndian>()?;
474        val.next_owner_key = EcdsaRawPublicKey::read(&mut reader).map_err(ChipDataError::Anyhow)?;
475
476        // SPX+ public key size is 32 bytes.
477        val.hybrid_padding.resize(32, 0);
478        reader.read_exact(&mut val.hybrid_padding)?;
479
480        val.signature = EcdsaRawSignature::read(&mut reader).map_err(ChipDataError::Anyhow)?;
481        Ok(val)
482    }
483}
484impl OwnershipUnlockRequest {
485    pub const SIZE: usize = 212;
486    const RESERVED_SIZE: usize = 8 * std::mem::size_of::<u32>();
487    const SIGNATURE_OFFSET: usize = 148;
488    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
489        dest.write_u32::<LittleEndian>(u32::from(self.unlock_mode))?;
490        dest.write_u64::<LittleEndian>(self.din)?;
491        for i in 0..Self::RESERVED_SIZE {
492            let p = self.reserved.get(i).unwrap_or(&0x00);
493            dest.write_all(std::slice::from_ref(p))?;
494        }
495        dest.write_u64::<LittleEndian>(self.nonce)?;
496        self.next_owner_key.write(dest)?;
497
498        // SPX+ public key size is 32 bytes.
499        for i in 0..32 {
500            let p = self.hybrid_padding.get(i).unwrap_or(&0x00);
501            dest.write_all(std::slice::from_ref(p))?;
502        }
503
504        self.signature.write(dest)?;
505        Ok(())
506    }
507
508    pub fn set_next_owner_key(&mut self, key: &EcdsaPublicKey) -> Result<()> {
509        self.next_owner_key = EcdsaRawPublicKey::try_from(key)?;
510        Ok(())
511    }
512
513    pub fn sign(&mut self, key: &EcdsaPrivateKey) -> Result<()> {
514        let mut data = Vec::new();
515        self.write(&mut data)?;
516        self.signature = key.digest_and_sign(&data[..Self::SIGNATURE_OFFSET])?;
517        Ok(())
518    }
519
520    pub fn detached_sign(
521        &mut self,
522        algorithm: OwnershipKeyAlg,
523        ecdsa_key: Option<&EcdsaPrivateKey>,
524        spx_key: Option<&SpxSecretKey>,
525    ) -> Result<DetachedSignature> {
526        self.signature = Default::default();
527        let mut data = Vec::new();
528        self.write(&mut data)?;
529        DetachedSignature::new(
530            &data[..Self::SIGNATURE_OFFSET],
531            BootSvcKind::OwnershipUnlockRequest.into(),
532            algorithm,
533            self.nonce,
534            ecdsa_key,
535            spx_key,
536        )
537    }
538}
539
540impl TryFrom<&[u8]> for OwnershipUnlockResponse {
541    type Error = ChipDataError;
542    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
543        let mut reader = std::io::Cursor::new(buf);
544        Ok(OwnershipUnlockResponse {
545            status: RomError(reader.read_u32::<LittleEndian>()?),
546        })
547    }
548}
549impl OwnershipUnlockResponse {
550    pub const SIZE: usize = 4;
551    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
552        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
553        Ok(())
554    }
555}
556
557impl TryFrom<&[u8]> for OwnershipActivateRequest {
558    type Error = ChipDataError;
559    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
560        let mut reader = std::io::Cursor::new(buf);
561        let mut val = Self::default();
562        val.primary_bl0_slot = BootSlot(reader.read_u32::<LittleEndian>()?);
563        val.din = reader.read_u64::<LittleEndian>()?;
564        val.erase_previous = HardenedBool(reader.read_u32::<LittleEndian>()?);
565        val.reserved.resize(Self::RESERVED_SIZE, 0);
566        reader.read_exact(&mut val.reserved)?;
567        val.nonce = reader.read_u64::<LittleEndian>()?;
568        val.signature = EcdsaRawSignature::read(&mut reader).map_err(ChipDataError::Anyhow)?;
569        Ok(val)
570    }
571}
572impl OwnershipActivateRequest {
573    pub const SIZE: usize = 212;
574    const RESERVED_SIZE: usize = 31 * std::mem::size_of::<u32>();
575    const SIGNATURE_OFFSET: usize = 148;
576    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
577        dest.write_u32::<LittleEndian>(u32::from(self.primary_bl0_slot))?;
578        dest.write_u64::<LittleEndian>(self.din)?;
579        dest.write_u32::<LittleEndian>(u32::from(self.erase_previous))?;
580        for i in 0..Self::RESERVED_SIZE {
581            let p = self.reserved.get(i).unwrap_or(&0x00);
582            dest.write_all(std::slice::from_ref(p))?;
583        }
584        dest.write_u64::<LittleEndian>(self.nonce)?;
585        self.signature.write(dest)?;
586        Ok(())
587    }
588
589    pub fn sign(&mut self, key: &EcdsaPrivateKey) -> Result<()> {
590        let mut data = Vec::new();
591        self.write(&mut data)?;
592        self.signature = key.digest_and_sign(&data[..Self::SIGNATURE_OFFSET])?;
593        Ok(())
594    }
595
596    pub fn detached_sign(
597        &mut self,
598        algorithm: OwnershipKeyAlg,
599        ecdsa_key: Option<&EcdsaPrivateKey>,
600        spx_key: Option<&SpxSecretKey>,
601    ) -> Result<DetachedSignature> {
602        self.signature = Default::default();
603        let mut data = Vec::new();
604        self.write(&mut data)?;
605        DetachedSignature::new(
606            &data[..Self::SIGNATURE_OFFSET],
607            BootSvcKind::OwnershipActivateRequest.into(),
608            algorithm,
609            self.nonce,
610            ecdsa_key,
611            spx_key,
612        )
613    }
614}
615
616impl TryFrom<&[u8]> for OwnershipActivateResponse {
617    type Error = ChipDataError;
618    fn try_from(buf: &[u8]) -> std::result::Result<Self, Self::Error> {
619        let mut reader = std::io::Cursor::new(buf);
620        Ok(OwnershipActivateResponse {
621            status: RomError(reader.read_u32::<LittleEndian>()?),
622        })
623    }
624}
625impl OwnershipActivateResponse {
626    pub const SIZE: usize = 4;
627    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
628        dest.write_u32::<LittleEndian>(u32::from(self.status))?;
629        Ok(())
630    }
631}