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