opentitanlib/crypto/
ecdsa.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::{Context, Result, anyhow, ensure};
6use ecdsa::Signature;
7use ecdsa::elliptic_curve::pkcs8::{DecodePrivateKey, EncodePrivateKey};
8use ecdsa::elliptic_curve::pkcs8::{DecodePublicKey, EncodePublicKey};
9use ecdsa::signature::hazmat::PrehashVerifier;
10use p256::NistP256;
11use p256::ecdsa::{SigningKey, VerifyingKey};
12use pem_rfc7468::Decoder;
13use rand::rngs::OsRng;
14use serde::Deserialize;
15use serde_annotate::Annotate;
16use sha2::digest::generic_array::GenericArray;
17use std::fs::File;
18use std::io::{Read, Write};
19use std::path::Path;
20use std::str::FromStr;
21
22use super::Error;
23use crate::crypto::sha256::Sha256Digest;
24use util::clean_pem_bytes_for_parsing;
25
26pub struct EcdsaPrivateKey {
27    pub key: SigningKey,
28}
29
30pub struct EcdsaPublicKey {
31    pub key: VerifyingKey,
32}
33
34impl Default for EcdsaPrivateKey {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40impl EcdsaPrivateKey {
41    pub fn new() -> Self {
42        Self {
43            key: SigningKey::random(&mut OsRng),
44        }
45    }
46
47    pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
48        self.key.write_pkcs8_der_file(path)?;
49        Ok(())
50    }
51
52    pub fn load(path: impl AsRef<Path>) -> Result<Self> {
53        let key = SigningKey::read_pkcs8_der_file(path)?;
54        Ok(Self { key })
55    }
56
57    pub fn public_key(&self) -> EcdsaPublicKey {
58        EcdsaPublicKey {
59            key: *self.key.verifying_key(),
60        }
61    }
62
63    pub fn sign(&self, digest: &Sha256Digest) -> Result<EcdsaRawSignature> {
64        let (sig, _) = self.key.sign_prehash_recoverable(digest.as_ref())?;
65        let bytes = sig.to_bytes();
66        let half = bytes.len() / 2;
67        // The signature bytes are (R || S).  Since opentitan is a little-endian
68        // machine, we want to reverse the byte order of each component of the
69        // signature.
70        let mut r = Vec::new();
71        r.extend(bytes[..half].iter().rev());
72        let mut s = Vec::new();
73        s.extend(bytes[half..].iter().rev());
74        Ok(EcdsaRawSignature { r, s })
75    }
76
77    pub fn digest_and_sign(&self, data: &[u8]) -> Result<EcdsaRawSignature> {
78        self.sign(&Sha256Digest::hash(data))
79    }
80}
81
82#[derive(Debug, Clone, Deserialize, Annotate, PartialEq)]
83pub struct EcdsaRawSignature {
84    #[serde(with = "serde_bytes")]
85    #[annotate(format = hexstr)]
86    pub r: Vec<u8>,
87    #[serde(with = "serde_bytes")]
88    #[annotate(format = hexstr)]
89    pub s: Vec<u8>,
90}
91
92impl Default for EcdsaRawSignature {
93    fn default() -> Self {
94        Self {
95            r: vec![0u8; 32],
96            s: vec![0u8; 32],
97        }
98    }
99}
100
101impl TryFrom<&[u8]> for EcdsaRawSignature {
102    type Error = Error;
103    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
104        if value.len() != Self::SIZE {
105            return Err(Error::InvalidSignature(anyhow!(
106                "bad length: {}",
107                value.len()
108            )));
109        }
110        let mut value = std::io::Cursor::new(value);
111        EcdsaRawSignature::read(&mut value).map_err(Error::InvalidSignature)
112    }
113}
114
115impl EcdsaRawSignature {
116    const SIZE: usize = 64;
117
118    pub fn read(src: &mut impl Read) -> Result<Self> {
119        let mut sig = Self::default();
120        src.read_exact(&mut sig.r)?;
121        src.read_exact(&mut sig.s)?;
122        Ok(sig)
123    }
124
125    pub fn read_from_file(path: &Path) -> Result<EcdsaRawSignature> {
126        let mut file =
127            File::open(path).with_context(|| format!("Failed to read file: {path:?}"))?;
128        let file_size = std::fs::metadata(path)
129            .with_context(|| format!("Failed to get metadata for {path:?}"))?
130            .len();
131
132        let raw_size = Self::SIZE as u64;
133        if file_size == raw_size {
134            // This must be a raw signature, just read it as is.
135            EcdsaRawSignature::read(&mut file)
136        } else {
137            let mut data = Vec::<u8>::new();
138
139            file.read_to_end(&mut data)
140                .with_context(|| format!("Failed to read {path:?}"))?;
141
142            // Let's try interpreting the file as ASN.1 DER.
143            // If unsuccessful, attempt PEM decoding.
144            EcdsaRawSignature::from_der(&data)
145                .or_else(|_| EcdsaRawSignature::from_pem(&data))
146                .with_context(|| format!("Failed parsing {path:?}"))
147        }
148    }
149
150    pub fn write(&self, dest: &mut impl Write) -> Result<usize> {
151        ensure!(
152            self.r.len() == 32,
153            Error::InvalidSignature(anyhow!("bad r length: {}", self.r.len()))
154        );
155        ensure!(
156            self.s.len() == 32,
157            Error::InvalidSignature(anyhow!("bad s length: {}", self.s.len()))
158        );
159        dest.write_all(&self.r)?;
160        dest.write_all(&self.s)?;
161        Ok(64)
162    }
163
164    pub fn to_vec(&self) -> Result<Vec<u8>> {
165        let mut sig = Vec::new();
166        self.write(&mut sig)?;
167        Ok(sig)
168    }
169
170    pub fn is_empty(&self) -> bool {
171        self.r.iter().all(|&v| v == 0) && self.s.iter().all(|&v| v == 0)
172    }
173
174    fn from_der(data: &[u8]) -> Result<EcdsaRawSignature> {
175        let sig = Signature::<NistP256>::from_der(data).with_context(|| "Failed to parse DER")?;
176
177        // R and S are integers in big endian format. The size of the numbers is
178        // not fixed, it could be 33 bytes in case the value has the top byte
179        // top bit set, and a leading zero has to be added to keep the value
180        // positive (50% chance), or it could be shorter than 32 bytes (1/256
181        // chance). Need to get around these issues and convert into little
182        // endian of exactly 32 bytes format expected by the firmware.
183        let mut r: Vec<u8> = sig.r().to_bytes().to_vec();
184        let mut s: Vec<u8> = sig.s().to_bytes().to_vec();
185
186        // Convert to little endian format.
187        r.reverse();
188        s.reverse();
189
190        r.resize(32, 0u8);
191        s.resize(32, 0u8);
192
193        Ok(EcdsaRawSignature { r, s })
194    }
195
196    fn from_pem(data: &[u8]) -> Result<EcdsaRawSignature> {
197        // Ensures valid PEM markers and a recognized label are present.
198        let _ = pem_rfc7468::decode_label(data)?;
199        let mut buf = Vec::new();
200        let result = Decoder::new(data);
201        match result {
202            Ok(mut decoder) => decoder.decode_to_end(&mut buf)?,
203            _ => {
204                let cleaned_data = clean_pem_bytes_for_parsing(data)?;
205                let mut decoder = Decoder::new(&cleaned_data)?;
206                decoder.decode_to_end(&mut buf)?
207            }
208        };
209        Self::from_der(buf.as_slice())
210    }
211}
212
213impl EcdsaPublicKey {
214    pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
215        self.key.write_public_key_der_file(path)?;
216        Ok(())
217    }
218
219    pub fn load(path: impl AsRef<Path>) -> Result<Self> {
220        let key = VerifyingKey::read_public_key_der_file(path)?;
221        Ok(Self { key })
222    }
223
224    pub fn verify(&self, digest: &Sha256Digest, signature: &EcdsaRawSignature) -> Result<()> {
225        let mut bytes = signature.to_vec()?;
226        let half = bytes.len() / 2;
227        // The signature bytes are (R || S).  Since opentitan is a little-endian
228        // machine, we expect the input signature to have R and S in
229        // little-endian order.  Reverse the bytes back to big-endian ordering.
230        bytes[..half].reverse();
231        bytes[half..].reverse();
232        let signature = Signature::from_slice(&bytes)?;
233        self.key.verify_prehash(digest.as_ref(), &signature)?;
234        Ok(())
235    }
236}
237
238impl TryFrom<&EcdsaRawPublicKey> for EcdsaPublicKey {
239    type Error = Error;
240    fn try_from(v: &EcdsaRawPublicKey) -> Result<Self, Self::Error> {
241        let mut x = v.x.clone();
242        let mut y = v.y.clone();
243
244        x.reverse();
245        y.reverse();
246
247        let key = VerifyingKey::from_encoded_point(&p256::EncodedPoint::from_affine_coordinates(
248            &GenericArray::from(<[u8; 32]>::try_from(x.as_slice()).unwrap()),
249            &GenericArray::from(<[u8; 32]>::try_from(y.as_slice()).unwrap()),
250            false,
251        ))
252        .map_err(|e| Error::Other(anyhow!(e)))
253        .context("Failed to create verifying key from raw public key")?;
254        Ok(Self { key })
255    }
256}
257
258#[derive(Debug, Deserialize, Annotate, PartialEq)]
259pub struct EcdsaRawPublicKey {
260    #[serde(with = "serde_bytes")]
261    #[annotate(format = hexstr)]
262    pub x: Vec<u8>,
263    #[serde(with = "serde_bytes")]
264    #[annotate(format = hexstr)]
265    pub y: Vec<u8>,
266}
267
268impl Default for EcdsaRawPublicKey {
269    fn default() -> Self {
270        Self {
271            x: vec![0u8; 32],
272            y: vec![0u8; 32],
273        }
274    }
275}
276
277impl TryFrom<&EcdsaPublicKey> for EcdsaRawPublicKey {
278    type Error = Error;
279    fn try_from(v: &EcdsaPublicKey) -> Result<Self, Self::Error> {
280        let point = v.key.to_encoded_point(false);
281        // Since opentitan is a little-endian machine, we reverse the byte
282        // order of the X and Y values.
283        let mut x = point.x().unwrap().as_slice().to_vec();
284        let mut y = point.y().unwrap().as_slice().to_vec();
285        x.reverse();
286        y.reverse();
287        Ok(EcdsaRawPublicKey { x, y })
288    }
289}
290
291impl TryFrom<EcdsaPublicKey> for EcdsaRawPublicKey {
292    type Error = Error;
293    fn try_from(v: EcdsaPublicKey) -> Result<Self, Self::Error> {
294        EcdsaRawPublicKey::try_from(&v)
295    }
296}
297
298impl FromStr for EcdsaRawPublicKey {
299    type Err = Error;
300    fn from_str(s: &str) -> Result<Self, Self::Err> {
301        let key = EcdsaPublicKey::load(s)
302            .with_context(|| format!("Failed to load {s}"))
303            .map_err(Error::Other)?;
304        EcdsaRawPublicKey::try_from(&key)
305    }
306}
307
308impl EcdsaRawPublicKey {
309    pub const SIZE: usize = 32 + 32;
310    pub fn read(src: &mut impl Read) -> Result<Self> {
311        let mut key = Self::default();
312        src.read_exact(&mut key.x)?;
313        src.read_exact(&mut key.y)?;
314        Ok(key)
315    }
316    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
317        ensure!(
318            self.x.len() == 32,
319            Error::InvalidPublicKey(anyhow!("bad x length: {}", self.x.len()))
320        );
321        ensure!(
322            self.y.len() == 32,
323            Error::InvalidPublicKey(anyhow!("bad y length: {}", self.y.len()))
324        );
325        dest.write_all(&self.x)?;
326        dest.write_all(&self.y)?;
327        Ok(())
328    }
329}