Skip to main content

rsa/pkcs1v15/
signing_key.rs

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