Skip to main content

rsa/pkcs1v15/
signing_key.rs

1use super::{pkcs1v15_generate_prefix, sign, Signature, VerifyingKey};
2use crate::{dummy_rng::DummyRng, Result, RsaPrivateKey};
3use alloc::vec::Vec;
4use const_oid::AssociatedOid;
5use core::marker::PhantomData;
6use digest::{Digest, FixedOutput, HashMarker, Update};
7use rand_core::{CryptoRng, TryCryptoRng};
8use signature::{
9    hazmat::PrehashSigner, DigestSigner, Keypair, MultipartSigner, RandomizedDigestSigner,
10    RandomizedMultipartSigner, RandomizedSigner, Signer,
11};
12use zeroize::ZeroizeOnDrop;
13
14#[cfg(feature = "encoding")]
15use {
16    super::oid,
17    pkcs8::{EncodePrivateKey, SecretDocument},
18    spki::{
19        der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
20        SignatureAlgorithmIdentifier,
21    },
22};
23#[cfg(feature = "serde")]
24use {
25    pkcs8::DecodePrivateKey,
26    serdect::serde::{de, ser, Deserialize, Serialize},
27};
28
29/// Signing key for `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2].
30///
31/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
32#[derive(Debug, Clone)]
33pub struct SigningKey<D>
34where
35    D: Digest,
36{
37    inner: RsaPrivateKey,
38    prefix: Vec<u8>,
39    phantom: PhantomData<D>,
40}
41
42impl<D> SigningKey<D>
43where
44    D: Digest + AssociatedOid,
45{
46    /// Create a new signing key with a prefix for the digest `D`.
47    pub fn new(key: RsaPrivateKey) -> Self {
48        Self {
49            inner: key,
50            prefix: pkcs1v15_generate_prefix::<D>(),
51            phantom: Default::default(),
52        }
53    }
54
55    /// Generate a new signing key with a prefix for the digest `D`.
56    pub fn random<R: CryptoRng + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
57        Ok(Self {
58            inner: RsaPrivateKey::new(rng, bit_size)?,
59            prefix: pkcs1v15_generate_prefix::<D>(),
60            phantom: Default::default(),
61        })
62    }
63}
64
65impl<D> SigningKey<D>
66where
67    D: Digest,
68{
69    /// Create a new signing key from the give RSA private key with an empty prefix.
70    ///
71    /// ## Note: unprefixed signatures are uncommon
72    ///
73    /// In most cases you'll want to use [`SigningKey::new`].
74    pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
75        Self {
76            inner: key,
77            prefix: Vec::new(),
78            phantom: Default::default(),
79        }
80    }
81
82    /// Generate a new signing key with an empty prefix.
83    pub fn random_unprefixed<R: CryptoRng + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
84        Ok(Self {
85            inner: RsaPrivateKey::new(rng, bit_size)?,
86            prefix: Vec::new(),
87            phantom: Default::default(),
88        })
89    }
90}
91
92//
93// `*Signer` trait impls
94//
95
96impl<D> DigestSigner<D, Signature> for SigningKey<D>
97where
98    D: Default + FixedOutput + HashMarker + Update,
99{
100    fn try_sign_digest<F: Fn(&mut D) -> signature::Result<()>>(
101        &self,
102        f: F,
103    ) -> signature::Result<Signature> {
104        let mut digest = D::default();
105        f(&mut digest)?;
106        sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize_fixed())?
107            .as_slice()
108            .try_into()
109    }
110}
111
112impl<D> PrehashSigner<Signature> for SigningKey<D>
113where
114    D: Digest,
115{
116    fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
117        sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
118            .as_slice()
119            .try_into()
120    }
121}
122
123impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
124where
125    D: Default + FixedOutput + HashMarker + Update,
126{
127    fn try_sign_digest_with_rng<
128        R: TryCryptoRng + ?Sized,
129        F: Fn(&mut D) -> signature::Result<()>,
130    >(
131        &self,
132        rng: &mut R,
133        f: F,
134    ) -> signature::Result<Signature> {
135        let mut digest = D::default();
136        f(&mut digest)?;
137        sign(
138            Some(rng),
139            &self.inner,
140            &self.prefix,
141            &digest.finalize_fixed(),
142        )?
143        .as_slice()
144        .try_into()
145    }
146}
147
148impl<D> RandomizedSigner<Signature> for SigningKey<D>
149where
150    D: Digest,
151{
152    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
153        &self,
154        rng: &mut R,
155        msg: &[u8],
156    ) -> signature::Result<Signature> {
157        self.try_multipart_sign_with_rng(rng, &[msg])
158    }
159}
160
161impl<D> RandomizedMultipartSigner<Signature> for SigningKey<D>
162where
163    D: Digest,
164{
165    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
166        &self,
167        rng: &mut R,
168        msg: &[&[u8]],
169    ) -> signature::Result<Signature> {
170        let mut digest = D::new();
171        msg.iter().for_each(|slice| digest.update(slice));
172        sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
173            .as_slice()
174            .try_into()
175    }
176}
177
178impl<D> Signer<Signature> for SigningKey<D>
179where
180    D: Digest,
181{
182    fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
183        self.try_multipart_sign(&[msg])
184    }
185}
186
187impl<D> MultipartSigner<Signature> for SigningKey<D>
188where
189    D: Digest,
190{
191    fn try_multipart_sign(&self, msg: &[&[u8]]) -> signature::Result<Signature> {
192        let mut digest = D::new();
193        msg.iter().for_each(|slice| digest.update(slice));
194        sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
195            .as_slice()
196            .try_into()
197    }
198}
199
200//
201// Other trait impls
202//
203
204impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
205where
206    D: Digest,
207{
208    fn as_ref(&self) -> &RsaPrivateKey {
209        &self.inner
210    }
211}
212
213#[cfg(feature = "encoding")]
214impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
215where
216    D: Digest,
217{
218    type Params = AnyRef<'static>;
219
220    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
221}
222
223#[cfg(feature = "encoding")]
224impl<D> EncodePrivateKey for SigningKey<D>
225where
226    D: Digest,
227{
228    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
229        self.inner.to_pkcs8_der()
230    }
231}
232
233impl<D> From<RsaPrivateKey> for SigningKey<D>
234where
235    D: Digest + AssociatedOid,
236{
237    fn from(key: RsaPrivateKey) -> Self {
238        Self::new(key)
239    }
240}
241
242impl<D> From<SigningKey<D>> for RsaPrivateKey
243where
244    D: Digest,
245{
246    fn from(key: SigningKey<D>) -> Self {
247        key.inner
248    }
249}
250
251impl<D> Keypair for SigningKey<D>
252where
253    D: Digest,
254{
255    type VerifyingKey = VerifyingKey<D>;
256
257    fn verifying_key(&self) -> Self::VerifyingKey {
258        VerifyingKey {
259            inner: self.inner.to_public_key(),
260            prefix: self.prefix.clone(),
261            phantom: Default::default(),
262        }
263    }
264}
265
266#[cfg(feature = "encoding")]
267impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
268where
269    D: Digest + oid::RsaSignatureAssociatedOid,
270{
271    type Params = AnyRef<'static>;
272
273    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
274        AlgorithmIdentifierRef {
275            oid: D::OID,
276            parameters: Some(AnyRef::NULL),
277        };
278}
279
280#[cfg(feature = "encoding")]
281impl<D> TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SigningKey<D>
282where
283    D: Digest + AssociatedOid,
284{
285    type Error = pkcs8::Error;
286
287    fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
288        private_key_info
289            .algorithm
290            .assert_algorithm_oid(pkcs1::ALGORITHM_OID)?;
291        RsaPrivateKey::try_from(private_key_info).map(Self::new)
292    }
293}
294
295impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
296
297impl<D> PartialEq for SigningKey<D>
298where
299    D: Digest,
300{
301    fn eq(&self, other: &Self) -> bool {
302        self.inner == other.inner && self.prefix == other.prefix
303    }
304}
305
306#[cfg(feature = "serde")]
307impl<D> Serialize for SigningKey<D>
308where
309    D: Digest,
310{
311    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
312    where
313        S: serdect::serde::Serializer,
314    {
315        let der = self.to_pkcs8_der().map_err(ser::Error::custom)?;
316        serdect::slice::serialize_hex_lower_or_bin(&der.as_bytes(), serializer)
317    }
318}
319
320#[cfg(feature = "serde")]
321impl<'de, D> Deserialize<'de> for SigningKey<D>
322where
323    D: Digest + AssociatedOid,
324{
325    fn deserialize<De>(deserializer: De) -> core::result::Result<Self, De::Error>
326    where
327        De: serdect::serde::Deserializer<'de>,
328    {
329        let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
330        Self::from_pkcs8_der(&der_bytes).map_err(de::Error::custom)
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    #[test]
337    #[cfg(all(feature = "hazmat", feature = "serde"))]
338    fn test_serde() {
339        use super::*;
340        use crate::RsaPrivateKey;
341        use rand::rngs::ChaCha8Rng;
342        use rand_core::SeedableRng;
343        use serde_test::{assert_tokens, Configure, Token};
344        use sha2::Sha256;
345
346        let mut rng = ChaCha8Rng::from_seed([42; 32]);
347        let priv_key = RsaPrivateKey::new_unchecked(&mut rng, 64).expect("failed to generate key");
348        let signing_key = SigningKey::<Sha256>::new(priv_key);
349
350        let tokens = [Token::Str(concat!(
351            "3056020100300d06092a864886f70d010101050004423040020100020900ab240c",
352            "3361d02e370203010001020811e54a15259d22f9020500ceff5cf3020500d3a7aa",
353            "ad020500ccaddf17020500cb529d3d020500bb526d6f",
354        ))];
355
356        assert_tokens(&signing_key.readable(), &tokens);
357    }
358}