opentitanlib/chip/
helper.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 clap::Args;
7use sphincsplus::{DecodeKey, SpxSecretKey};
8use std::fs::File;
9use std::io::Read;
10use std::path::PathBuf;
11
12use crate::chip::boot_svc::{
13    BootSlot, OwnershipActivateRequest, OwnershipUnlockRequest, UnlockMode,
14};
15use crate::crypto::ecdsa::{EcdsaPrivateKey, EcdsaPublicKey, EcdsaRawPublicKey, EcdsaRawSignature};
16use crate::ownership::{DetachedSignature, OwnershipKeyAlg};
17use crate::util::parse_int::ParseInt;
18
19#[derive(Debug, Default, Args)]
20pub struct OwnershipUnlockParams {
21    #[arg(long, value_enum, help = "Requested unlock mode")]
22    pub mode: Option<UnlockMode>,
23    #[arg(long, value_parser = u64::from_str, help="Current ROM_EXT nonce")]
24    pub nonce: Option<u64>,
25    #[arg(long, value_parser = u64::from_str, help="Device Identification Number of the chip")]
26    pub din: Option<u64>,
27    #[arg(long, help = "A path to the next owner key (for endorsed mode)")]
28    pub next_owner: Option<PathBuf>,
29    #[arg(
30        long,
31        help = "A path to an external ECDSA signature for the unlock request"
32    )]
33    pub signature: Option<PathBuf>,
34    #[arg(long, default_value_t = OwnershipKeyAlg::EcdsaP256, help = "The algorithm used to sign the request")]
35    pub algorithm: OwnershipKeyAlg,
36    #[arg(long, help = "A path to a private ECDSA key to sign the request")]
37    pub ecdsa_key: Option<PathBuf>,
38    #[arg(long, help = "A path to a private SPX key to sign the request")]
39    pub spx_key: Option<PathBuf>,
40    #[arg(long, help = "Enable detached signature")]
41    pub enable_detached_sig: bool,
42}
43
44impl OwnershipUnlockParams {
45    /// Applies the parameters to the unlock request.
46    pub fn apply(&self, unlock: &mut OwnershipUnlockRequest) -> Result<Option<DetachedSignature>> {
47        if let Some(mode) = &self.mode {
48            unlock.unlock_mode = *mode;
49        }
50        if let Some(nonce) = &self.nonce {
51            unlock.nonce = *nonce;
52        }
53        if let Some(din) = &self.din {
54            unlock.din = *din;
55        }
56        if let Some(next_owner) = &self.next_owner {
57            let key = EcdsaPublicKey::load(next_owner)?;
58            // TODO: Support transferring to a different algorithm.
59            unlock.next_owner_alg = self.algorithm;
60            unlock.next_owner_key = EcdsaRawPublicKey::try_from(&key)?;
61        }
62        if let Some(signature) = &self.signature {
63            // TODO: Recognize both a raw ECDSA signature and/or a detached signature struct
64            // containing only an ECDSA signature.
65            let mut f = File::open(signature)?;
66            unlock.signature = EcdsaRawSignature::read(&mut f)?;
67        }
68        if self.signature.is_none() && (self.ecdsa_key.is_some() || self.spx_key.is_some()) {
69            let ecdsa_key = self
70                .ecdsa_key
71                .as_ref()
72                .map(EcdsaPrivateKey::load)
73                .transpose()?;
74            let spx_key = self
75                .spx_key
76                .as_ref()
77                .map(SpxSecretKey::read_pem_file)
78                .transpose()?;
79            let signature =
80                unlock.detached_sign(self.algorithm, ecdsa_key.as_ref(), spx_key.as_ref())?;
81            if !self.algorithm.is_detached() {
82                unlock.signature = signature.ecdsa.clone().expect("ECDSA signature");
83            }
84            Ok(Some(signature))
85        } else {
86            Ok(None)
87        }
88    }
89
90    /// Reads an unlock request (or creates a default request) and applies the aprameters.
91    pub fn apply_to(
92        &self,
93        reader: Option<&mut impl Read>,
94    ) -> Result<(OwnershipUnlockRequest, Option<DetachedSignature>)> {
95        let mut unlock = if let Some(r) = reader {
96            let mut data = Vec::new();
97            r.read_to_end(&mut data)?;
98            OwnershipUnlockRequest::try_from(data.as_slice())?
99        } else {
100            OwnershipUnlockRequest::default()
101        };
102        let signature = self.apply(&mut unlock)?;
103        if self.enable_detached_sig {
104            // Overwrite the signature to support the detached signature.
105            unlock.signature = EcdsaRawSignature::default();
106        }
107        Ok((unlock, signature))
108    }
109}
110
111#[derive(Debug, Default, Args)]
112pub struct OwnershipActivateParams {
113    #[arg(long, value_parser = u64::from_str, help="Current ROM_EXT nonce")]
114    pub nonce: Option<u64>,
115    #[arg(long, value_parser = u64::from_str, help="Device Identification Number of the chip")]
116    pub din: Option<u64>,
117    #[arg(
118        long,
119        help = "A path to an external ECDSA signature for the activate request"
120    )]
121    pub signature: Option<PathBuf>,
122    #[arg(long, default_value_t = OwnershipKeyAlg::EcdsaP256, help = "The algorithm used to sign the request")]
123    pub algorithm: OwnershipKeyAlg,
124    #[arg(long, help = "A path to a private ECDSA key to sign the request")]
125    pub ecdsa_key: Option<PathBuf>,
126    #[arg(long, help = "A path to a private SPX key to sign the request")]
127    pub spx_key: Option<PathBuf>,
128    #[arg(long, default_value = "SlotA", help = "Which bl0 slot to activate")]
129    pub primary_bl0_slot: BootSlot,
130}
131
132impl OwnershipActivateParams {
133    /// Applies the parameters to the activate request.
134    pub fn apply(
135        &self,
136        activate: &mut OwnershipActivateRequest,
137    ) -> Result<Option<DetachedSignature>> {
138        if let Some(nonce) = &self.nonce {
139            activate.nonce = *nonce;
140        }
141        if let Some(din) = &self.din {
142            activate.din = *din;
143        }
144        if let Some(signature) = &self.signature {
145            // TODO: Recognize both a raw ECDSA signature and/or a detached signature struct
146            // containing only an ECDSA signature.
147            let mut f = File::open(signature)?;
148            activate.signature = EcdsaRawSignature::read(&mut f)?;
149        }
150        activate.primary_bl0_slot = self.primary_bl0_slot;
151        if self.ecdsa_key.is_some() || self.spx_key.is_some() {
152            let ecdsa_key = self
153                .ecdsa_key
154                .as_ref()
155                .map(EcdsaPrivateKey::load)
156                .transpose()?;
157            let spx_key = self
158                .spx_key
159                .as_ref()
160                .map(SpxSecretKey::read_pem_file)
161                .transpose()?;
162            let signature =
163                activate.detached_sign(self.algorithm, ecdsa_key.as_ref(), spx_key.as_ref())?;
164            if !self.algorithm.is_detached() {
165                activate.signature = signature.ecdsa.clone().expect("ECDSA signature");
166            }
167            Ok(Some(signature))
168        } else {
169            Ok(None)
170        }
171    }
172
173    /// Reads an activate request (or creates a default request) and applies the parameters.
174    pub fn apply_to(
175        &self,
176        reader: Option<&mut impl Read>,
177    ) -> Result<(OwnershipActivateRequest, Option<DetachedSignature>)> {
178        let mut activate = if let Some(r) = reader {
179            let mut data = Vec::new();
180            r.read_to_end(&mut data)?;
181            OwnershipActivateRequest::try_from(data.as_slice())?
182        } else {
183            OwnershipActivateRequest::default()
184        };
185        let signature = self.apply(&mut activate)?;
186        Ok((activate, signature))
187    }
188}