rust_auth_utils/
ecdsa.rs

1// based on https://github.com/better-auth/utils/blob/main/src/ecdsa.ts
2
3use crate::types::{ECDSACurve, ExportKeyFormat, SHAFamily};
4use generic_array::GenericArray;
5use p256::ecdsa::{SigningKey, VerifyingKey};
6use rand_core::OsRng;
7use signature::{Signer, Verifier};
8
9pub struct ECDSA;
10
11impl ECDSA {
12    pub async fn generate_key_pair(
13        curve: Option<ECDSACurve>,
14    ) -> Result<(Vec<u8>, Vec<u8>), Box<dyn std::error::Error>> {
15        let curve = curve.unwrap_or(ECDSACurve::P256);
16
17        match curve {
18            ECDSACurve::P256 => {
19                let signing_key = SigningKey::random(&mut OsRng);
20                let verifying_key = signing_key.verifying_key();
21
22                let private_key = signing_key.to_bytes().to_vec();
23                let public_key = verifying_key.to_encoded_point(false).as_bytes().to_vec();
24
25                Ok((private_key, public_key))
26            }
27            _ => Err("Currently only P-256 curve is supported".into()),
28        }
29    }
30
31    pub async fn import_private_key(
32        private_key: impl AsRef<[u8]>,
33        curve: ECDSACurve,
34        _extractable: bool,
35    ) -> Result<SigningKey, Box<dyn std::error::Error>> {
36        match curve {
37            ECDSACurve::P256 => {
38                let bytes = private_key.as_ref();
39                let array_ref = GenericArray::from_slice(bytes);
40                let key = SigningKey::from_bytes(array_ref)?;
41                Ok(key)
42            }
43            _ => Err("Currently only P-256 curve is supported".into()),
44        }
45    }
46
47    pub async fn import_public_key(
48        public_key: impl AsRef<[u8]>,
49        curve: ECDSACurve,
50        _extractable: bool,
51    ) -> Result<VerifyingKey, Box<dyn std::error::Error>> {
52        match curve {
53            ECDSACurve::P256 => {
54                let key = VerifyingKey::from_sec1_bytes(public_key.as_ref())?;
55                Ok(key)
56            }
57            _ => Err("Currently only P-256 curve is supported".into()),
58        }
59    }
60
61    pub async fn sign(
62        private_key: &SigningKey,
63        data: impl AsRef<[u8]>,
64        hash: Option<SHAFamily>,
65    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
66        let hash = hash.unwrap_or(SHAFamily::SHA256);
67
68        match hash {
69            SHAFamily::SHA256 => {
70                let signature: p256::ecdsa::Signature = private_key.sign(data.as_ref());
71                Ok(signature.to_vec())
72            }
73            _ => Err("Currently only SHA-256 is supported for signing".into()),
74        }
75    }
76
77    pub async fn verify(
78        public_key: &VerifyingKey,
79        signature: impl AsRef<[u8]>,
80        data: impl AsRef<[u8]>,
81        hash: Option<SHAFamily>,
82    ) -> Result<bool, Box<dyn std::error::Error>> {
83        let hash = hash.unwrap_or(SHAFamily::SHA256);
84
85        match hash {
86            SHAFamily::SHA256 => {
87                let sig = p256::ecdsa::Signature::from_slice(signature.as_ref())?;
88                Ok(public_key.verify(data.as_ref(), &sig).is_ok())
89            }
90            _ => Err("Currently only SHA-256 is supported for verification".into()),
91        }
92    }
93
94    pub async fn export_key(
95        key: &(impl AsRef<[u8]> + ?Sized),
96        format: ExportKeyFormat,
97    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
98        match format {
99            ExportKeyFormat::SPKI => {
100                if let Ok(public_key) = VerifyingKey::from_sec1_bytes(key.as_ref()) {
101                    Ok(public_key.to_encoded_point(false).as_bytes().to_vec())
102                } else {
103                    Err("Key is not a valid public key".into())
104                }
105            }
106            ExportKeyFormat::PKCS8 => {
107                let array_ref = GenericArray::from_slice(key.as_ref());
108                if let Ok(private_key) = SigningKey::from_bytes(array_ref) {
109                    Ok(private_key.to_bytes().to_vec())
110                } else {
111                    Err("Key is not a valid private key".into())
112                }
113            }
114            _ => Err("Unsupported export format".into()),
115        }
116    }
117
118    pub async fn export_key_private(
119        key: &SigningKey,
120        format: ExportKeyFormat,
121    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
122        match format {
123            ExportKeyFormat::PKCS8 => Ok(key.to_bytes().to_vec()),
124            _ => Err("Unsupported export format for private key".into()),
125        }
126    }
127
128    pub async fn export_key_public(
129        key: &VerifyingKey,
130        format: ExportKeyFormat,
131    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
132        match format {
133            ExportKeyFormat::SPKI => Ok(key.to_encoded_point(false).as_bytes().to_vec()),
134            _ => Err("Unsupported export format for public key".into()),
135        }
136    }
137}