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            // TODO: Support transferring to a different algorithm.
55            unlock.next_owner_alg = self.algorithm;
56            unlock.next_owner_key = EcdsaRawPublicKey::try_from(&key)?;
57        }
58        if let Some(signature) = &self.signature {
59            // TODO: Recognize both a raw ECDSA signature and/or a detached signature struct
60            // containing only an ECDSA signature.
61            let mut f = File::open(signature)?;
62            unlock.signature = EcdsaRawSignature::read(&mut f)?;
63        }
64        if self.ecdsa_key.is_some() || self.spx_key.is_some() {
65            let ecdsa_key = self
66                .ecdsa_key
67                .as_ref()
68                .map(EcdsaPrivateKey::load)
69                .transpose()?;
70            let spx_key = self
71                .spx_key
72                .as_ref()
73                .map(SpxSecretKey::read_pem_file)
74                .transpose()?;
75            let signature =
76                unlock.detached_sign(self.algorithm, ecdsa_key.as_ref(), spx_key.as_ref())?;
77            if !self.algorithm.is_detached() {
78                unlock.signature = signature.ecdsa.clone().expect("ECDSA signature");
79            }
80            Ok(Some(signature))
81        } else {
82            Ok(None)
83        }
84    }
85
86    /// Reads an unlock request (or creates a default request) and applies the aprameters.
87    pub fn apply_to(
88        &self,
89        reader: Option<&mut impl Read>,
90    ) -> Result<(OwnershipUnlockRequest, Option<DetachedSignature>)> {
91        let mut unlock = if let Some(r) = reader {
92            let mut data = Vec::new();
93            r.read_to_end(&mut data)?;
94            OwnershipUnlockRequest::try_from(data.as_slice())?
95        } else {
96            OwnershipUnlockRequest::default()
97        };
98        let signature = self.apply(&mut unlock)?;
99        Ok((unlock, signature))
100    }
101}
102
103#[derive(Debug, Default, Args)]
104pub struct OwnershipActivateParams {
105    #[arg(long, value_parser = u64::from_str, help="Current ROM_EXT nonce")]
106    pub nonce: Option<u64>,
107    #[arg(long, value_parser = u64::from_str, help="Device Identification Number of the chip")]
108    pub din: Option<u64>,
109    #[arg(
110        long,
111        help = "A path to an external ECDSA signature for the activate request"
112    )]
113    pub signature: Option<PathBuf>,
114    #[arg(long, default_value_t = OwnershipKeyAlg::EcdsaP256, help = "The algorithm used to sign the request")]
115    pub algorithm: OwnershipKeyAlg,
116    #[arg(long, help = "A path to a private ECDSA key to sign the request")]
117    pub ecdsa_key: Option<PathBuf>,
118    #[arg(long, help = "A path to a private SPX key to sign the request")]
119    pub spx_key: Option<PathBuf>,
120}
121
122impl OwnershipActivateParams {
123    /// Applies the parameters to the activate request.
124    pub fn apply(
125        &self,
126        activate: &mut OwnershipActivateRequest,
127    ) -> Result<Option<DetachedSignature>> {
128        if let Some(nonce) = &self.nonce {
129            activate.nonce = *nonce;
130        }
131        if let Some(din) = &self.din {
132            activate.din = *din;
133        }
134        if let Some(signature) = &self.signature {
135            // TODO: Recognize both a raw ECDSA signature and/or a detached signature struct
136            // containing only an ECDSA signature.
137            let mut f = File::open(signature)?;
138            activate.signature = EcdsaRawSignature::read(&mut f)?;
139        }
140        if self.ecdsa_key.is_some() || self.spx_key.is_some() {
141            let ecdsa_key = self
142                .ecdsa_key
143                .as_ref()
144                .map(EcdsaPrivateKey::load)
145                .transpose()?;
146            let spx_key = self
147                .spx_key
148                .as_ref()
149                .map(SpxSecretKey::read_pem_file)
150                .transpose()?;
151            let signature =
152                activate.detached_sign(self.algorithm, ecdsa_key.as_ref(), spx_key.as_ref())?;
153            if !self.algorithm.is_detached() {
154                activate.signature = signature.ecdsa.clone().expect("ECDSA signature");
155            }
156            Ok(Some(signature))
157        } else {
158            Ok(None)
159        }
160    }
161
162    /// Reads an activate request (or creates a default request) and applies the parameters.
163    pub fn apply_to(
164        &self,
165        reader: Option<&mut impl Read>,
166    ) -> Result<(OwnershipActivateRequest, Option<DetachedSignature>)> {
167        let mut activate = if let Some(r) = reader {
168            let mut data = Vec::new();
169            r.read_to_end(&mut data)?;
170            OwnershipActivateRequest::try_from(data.as_slice())?
171        } else {
172            OwnershipActivateRequest::default()
173        };
174        let signature = self.apply(&mut activate)?;
175        Ok((activate, signature))
176    }
177}