Skip to main content

ring_native_ossl/
signature.rs

1//! Digital signature creation and verification, mirroring `ring::signature`.
2//!
3//! Covers ECDSA (P-256 and P-384 with SHA-256/384), Ed25519, and RSA (PKCS#1
4//! and PSS padding, SHA-256/384/512).  For RSA public key input, callers must
5//! supply a full `SubjectPublicKeyInfo` DER blob; for EC and Ed25519 the raw key
6//! bytes are accepted and the SPKI wrapper is added internally.
7
8use crate::error::{KeyRejected, Unspecified};
9use native_ossl::digest::DigestAlg;
10use native_ossl::params::ParamBuilder;
11use native_ossl::pkey::{KeygenCtx, Pkey, Private, Public, SignInit, Signer, Verifier};
12use std::ffi::CStr;
13
14use crate::spki::{ED25519_SPKI_HEADER, P256_SPKI_HEADER, P384_SPKI_HEADER};
15
16// ── Algorithm descriptor ──────────────────────────────────────────────────────
17
18/// A signature algorithm (mirrors `ring::signature::Algorithm`).
19///
20/// All algorithm properties live in this struct so callers never have to
21/// match on an internal enum — every operation reads the needed value
22/// directly from the descriptor.
23#[derive(Debug)]
24pub struct Algorithm {
25    /// Display name used in Debug output.
26    pub(crate) name: &'static str,
27    /// EC curve (e.g. `c"P-256"`). `None` for RSA and Ed25519.
28    pub(crate) ec_curve: Option<&'static CStr>,
29    /// SPKI DER prefix that wraps the raw public-key bytes.
30    /// `None` for RSA, where the caller supplies full SPKI DER directly.
31    pub(crate) spki_header: Option<&'static [u8]>,
32    /// Digest algorithm for signing/verifying. `None` for Ed25519 (prehash-free).
33    pub(crate) digest_name: Option<&'static CStr>,
34    /// `true` for RSA-PSS; `false` for ECDSA, Ed25519, and RSA-PKCS1.
35    pub(crate) rsa_pss: bool,
36}
37
38pub static ECDSA_P256_SHA256_ASN1: Algorithm = Algorithm {
39    name: "ECDSA_P256_SHA256_ASN1",
40    ec_curve: Some(c"P-256"),
41    spki_header: Some(P256_SPKI_HEADER),
42    digest_name: Some(c"SHA2-256"),
43    rsa_pss: false,
44};
45pub static ECDSA_P384_SHA384_ASN1: Algorithm = Algorithm {
46    name: "ECDSA_P384_SHA384_ASN1",
47    ec_curve: Some(c"P-384"),
48    spki_header: Some(P384_SPKI_HEADER),
49    digest_name: Some(c"SHA2-384"),
50    rsa_pss: false,
51};
52pub static ED25519: Algorithm = Algorithm {
53    name: "ED25519",
54    ec_curve: None,
55    spki_header: Some(ED25519_SPKI_HEADER),
56    digest_name: None,
57    rsa_pss: false,
58};
59pub static RSA_PKCS1_SHA256: Algorithm = Algorithm {
60    name: "RSA_PKCS1_SHA256",
61    ec_curve: None,
62    spki_header: None,
63    digest_name: Some(c"SHA2-256"),
64    rsa_pss: false,
65};
66pub static RSA_PKCS1_SHA384: Algorithm = Algorithm {
67    name: "RSA_PKCS1_SHA384",
68    ec_curve: None,
69    spki_header: None,
70    digest_name: Some(c"SHA2-384"),
71    rsa_pss: false,
72};
73pub static RSA_PKCS1_SHA512: Algorithm = Algorithm {
74    name: "RSA_PKCS1_SHA512",
75    ec_curve: None,
76    spki_header: None,
77    digest_name: Some(c"SHA2-512"),
78    rsa_pss: false,
79};
80pub static RSA_PSS_SHA256: Algorithm = Algorithm {
81    name: "RSA_PSS_SHA256",
82    ec_curve: None,
83    spki_header: None,
84    digest_name: Some(c"SHA2-256"),
85    rsa_pss: true,
86};
87pub static RSA_PSS_SHA384: Algorithm = Algorithm {
88    name: "RSA_PSS_SHA384",
89    ec_curve: None,
90    spki_header: None,
91    digest_name: Some(c"SHA2-384"),
92    rsa_pss: true,
93};
94pub static RSA_PSS_SHA512: Algorithm = Algorithm {
95    name: "RSA_PSS_SHA512",
96    ec_curve: None,
97    spki_header: None,
98    digest_name: Some(c"SHA2-512"),
99    rsa_pss: true,
100};
101
102// ── Signature output ──────────────────────────────────────────────────────────
103
104/// A DER-encoded signature (mirrors `ring::signature::Signature`).
105#[derive(Debug)]
106pub struct Signature(Vec<u8>);
107
108impl AsRef<[u8]> for Signature {
109    fn as_ref(&self) -> &[u8] {
110        &self.0
111    }
112}
113
114// ── EcdsaKeyPair ─────────────────────────────────────────────────────────────
115
116/// An ECDSA signing key pair (mirrors `ring::signature::EcdsaKeyPair`).
117pub struct EcdsaKeyPair {
118    alg: &'static Algorithm,
119    priv_key: Pkey<Private>,
120    pub_key_bytes: Vec<u8>,
121}
122
123impl std::fmt::Debug for EcdsaKeyPair {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        f.debug_struct("EcdsaKeyPair")
126            .field("alg", &self.alg.name)
127            .finish_non_exhaustive()
128    }
129}
130
131impl EcdsaKeyPair {
132    /// Generate a new ECDSA key pair.
133    ///
134    /// # Errors
135    ///
136    /// Returns `Unspecified` if OpenSSL key generation fails or the algorithm has no EC curve.
137    pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> {
138        let curve = alg.ec_curve.ok_or(Unspecified)?;
139        let spki_header = alg.spki_header.ok_or(Unspecified)?;
140
141        let mut ctx = KeygenCtx::new(c"EC").map_err(|_| Unspecified)?;
142        let params = ParamBuilder::new()
143            .and_then(|b| b.push_utf8_string(c"group", curve))
144            .and_then(ParamBuilder::build)
145            .map_err(|_| Unspecified)?;
146        ctx.set_params(&params).map_err(|_| Unspecified)?;
147        let priv_key = ctx.generate().map_err(|_| Unspecified)?;
148
149        let spki = priv_key.public_key_to_der().map_err(|_| Unspecified)?;
150        let raw_pub = spki.get(spki_header.len()..).ok_or(Unspecified)?.to_vec();
151
152        Ok(Self {
153            alg,
154            priv_key,
155            pub_key_bytes: raw_pub,
156        })
157    }
158
159    /// Load an ECDSA key pair from a PKCS#8 DER-encoded private key.
160    ///
161    /// # Errors
162    ///
163    /// Returns `KeyRejected` if the DER is malformed, the key is not EC, or the curve mismatches.
164    pub fn from_pkcs8(
165        alg: &'static Algorithm,
166        pkcs8: &[u8],
167        _rng: &dyn crate::rand::SecureRandom,
168    ) -> Result<Self, KeyRejected> {
169        let spki_header = alg
170            .spki_header
171            .ok_or_else(|| KeyRejected::new("wrong algorithm kind for ECDSA"))?;
172
173        let priv_key =
174            Pkey::<Private>::from_der(pkcs8).map_err(|_| KeyRejected::new("bad PKCS8 DER"))?;
175        if !priv_key.is_a(c"EC") {
176            return Err(KeyRejected::new("not an EC key"));
177        }
178        // Verify the curve matches by checking the SPKI DER header.
179        let spki = priv_key
180            .public_key_to_der()
181            .map_err(|_| KeyRejected::new("public_key_to_der failed"))?;
182        if !spki.starts_with(spki_header) {
183            return Err(KeyRejected::new("wrong EC curve"));
184        }
185        let raw_pub = spki[spki_header.len()..].to_vec();
186
187        Ok(Self {
188            alg,
189            priv_key,
190            pub_key_bytes: raw_pub,
191        })
192    }
193
194    #[must_use]
195    pub fn public_key(&self) -> &[u8] {
196        &self.pub_key_bytes
197    }
198
199    /// Sign `message` with this key pair.
200    ///
201    /// # Errors
202    ///
203    /// Returns `Unspecified` if the signing operation fails.
204    pub fn sign(
205        &self,
206        _rng: &dyn crate::rand::SecureRandom,
207        message: &[u8],
208    ) -> Result<Signature, Unspecified> {
209        let digest_name = self.alg.digest_name.ok_or(Unspecified)?;
210        let digest_alg = DigestAlg::fetch(digest_name, None).map_err(|_| Unspecified)?;
211        let init = SignInit {
212            digest: Some(&digest_alg),
213            params: None,
214        };
215        let mut signer = Signer::new(&self.priv_key, &init).map_err(|_| Unspecified)?;
216        let sig = signer.sign_oneshot(message).map_err(|_| Unspecified)?;
217        Ok(Signature(sig))
218    }
219}
220
221// ── Ed25519KeyPair ────────────────────────────────────────────────────────────
222
223/// An Ed25519 signing key pair (mirrors `ring::signature::Ed25519KeyPair`).
224pub struct Ed25519KeyPair {
225    priv_key: Pkey<Private>,
226    pub_key_bytes: Vec<u8>,
227}
228
229impl std::fmt::Debug for Ed25519KeyPair {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        f.debug_struct("Ed25519KeyPair").finish_non_exhaustive()
232    }
233}
234
235impl Ed25519KeyPair {
236    /// Generate a new Ed25519 key pair.
237    ///
238    /// # Errors
239    ///
240    /// Returns `Unspecified` if OpenSSL key generation fails.
241    pub fn generate() -> Result<Self, Unspecified> {
242        let mut ctx = KeygenCtx::new(c"ED25519").map_err(|_| Unspecified)?;
243        let priv_key = ctx.generate().map_err(|_| Unspecified)?;
244        let spki = priv_key.public_key_to_der().map_err(|_| Unspecified)?;
245        let raw_pub = spki
246            .get(ED25519_SPKI_HEADER.len()..)
247            .ok_or(Unspecified)?
248            .to_vec();
249        Ok(Self {
250            priv_key,
251            pub_key_bytes: raw_pub,
252        })
253    }
254
255    /// Load an Ed25519 key pair from a PKCS#8 DER-encoded private key.
256    ///
257    /// # Errors
258    ///
259    /// Returns `KeyRejected` if the DER is malformed or the key is not Ed25519.
260    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
261        let priv_key =
262            Pkey::<Private>::from_der(pkcs8).map_err(|_| KeyRejected::new("bad PKCS8 DER"))?;
263        if !priv_key.is_a(c"ED25519") {
264            return Err(KeyRejected::new("not an Ed25519 key"));
265        }
266        let spki = priv_key
267            .public_key_to_der()
268            .map_err(|_| KeyRejected::new("public_key_to_der failed"))?;
269        let raw_pub = spki
270            .get(ED25519_SPKI_HEADER.len()..)
271            .ok_or_else(|| KeyRejected::new("unexpected SPKI layout"))?
272            .to_vec();
273        Ok(Self {
274            priv_key,
275            pub_key_bytes: raw_pub,
276        })
277    }
278
279    #[must_use]
280    pub fn public_key(&self) -> &[u8] {
281        &self.pub_key_bytes
282    }
283
284    /// Sign `message`.
285    ///
286    /// # Errors
287    ///
288    /// Returns `Unspecified` if the signing operation fails.
289    pub fn sign(&self, message: &[u8]) -> Result<Signature, Unspecified> {
290        let init = SignInit {
291            digest: None,
292            params: None,
293        };
294        let mut signer = Signer::new(&self.priv_key, &init).map_err(|_| Unspecified)?;
295        let sig = signer.sign_oneshot(message).map_err(|_| Unspecified)?;
296        Ok(Signature(sig))
297    }
298}
299
300// ── UnparsedPublicKey + verify ────────────────────────────────────────────────
301
302/// A raw public key paired with its algorithm (mirrors `ring::signature::UnparsedPublicKey`).
303pub struct UnparsedPublicKey<'a> {
304    alg: &'static Algorithm,
305    bytes: &'a [u8],
306}
307
308impl<'a> UnparsedPublicKey<'a> {
309    #[must_use]
310    pub fn new(algorithm: &'static Algorithm, bytes: &'a [u8]) -> Self {
311        Self {
312            alg: algorithm,
313            bytes,
314        }
315    }
316}
317
318/// Verify `signature` over `message` using `public_key` and `algorithm`.
319///
320/// # Errors
321///
322/// Returns `Unspecified` if verification fails or the public key is invalid.
323pub fn verify(
324    algorithm: &'static Algorithm,
325    public_key: &[u8],
326    message: &[u8],
327    signature: &[u8],
328) -> Result<(), Unspecified> {
329    UnparsedPublicKey::new(algorithm, public_key).verify(message, signature)
330}
331
332impl UnparsedPublicKey<'_> {
333    /// # Errors
334    ///
335    /// Returns `Unspecified` if verification fails or the public key is invalid.
336    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Unspecified> {
337        let pub_key = load_public_key(self.alg, self.bytes)?;
338        verify_with_key(self.alg, &pub_key, message, signature)
339    }
340}
341
342fn load_public_key(alg: &'static Algorithm, raw: &[u8]) -> Result<Pkey<Public>, Unspecified> {
343    let spki = match alg.spki_header {
344        Some(header) => {
345            let mut v = header.to_vec();
346            v.extend_from_slice(raw);
347            v
348        }
349        // RSA: caller supplies full SubjectPublicKeyInfo DER.
350        None => raw.to_vec(),
351    };
352    Pkey::<Public>::from_der(&spki).map_err(|_| Unspecified)
353}
354
355fn verify_with_key(
356    alg: &'static Algorithm,
357    pub_key: &Pkey<Public>,
358    message: &[u8],
359    signature: &[u8],
360) -> Result<(), Unspecified> {
361    match alg.digest_name {
362        None => {
363            // Ed25519: no pre-hashing.
364            let init = SignInit {
365                digest: None,
366                params: None,
367            };
368            let mut verifier = Verifier::new(pub_key, &init).map_err(|_| Unspecified)?;
369            let ok = verifier
370                .verify_oneshot(message, signature)
371                .map_err(|_| Unspecified)?;
372            if ok {
373                Ok(())
374            } else {
375                Err(Unspecified)
376            }
377        }
378        Some(digest_name) => verify_digest(pub_key, digest_name, alg.rsa_pss, message, signature),
379    }
380}
381
382fn verify_digest(
383    pub_key: &Pkey<Public>,
384    digest_name: &'static CStr,
385    rsa_pss: bool,
386    message: &[u8],
387    signature: &[u8],
388) -> Result<(), Unspecified> {
389    let digest_alg = DigestAlg::fetch(digest_name, None).map_err(|_| Unspecified)?;
390
391    let params = if rsa_pss {
392        let p = ParamBuilder::new()
393            .and_then(|b| b.push_utf8_string(c"pad-mode", c"pss"))
394            .and_then(ParamBuilder::build)
395            .map_err(|_| Unspecified)?;
396        Some(p)
397    } else {
398        None
399    };
400
401    let init = SignInit {
402        digest: Some(&digest_alg),
403        params: params.as_ref(),
404    };
405    let mut verifier = Verifier::new(pub_key, &init).map_err(|_| Unspecified)?;
406    let ok = verifier
407        .verify_oneshot(message, signature)
408        .map_err(|_| Unspecified)?;
409    if ok {
410        Ok(())
411    } else {
412        Err(Unspecified)
413    }
414}