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