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