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