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