rsa/pss/
blinded_signing_key.rs

1use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey};
2use crate::{Result, RsaPrivateKey};
3use const_oid::AssociatedOid;
4use core::marker::PhantomData;
5use digest::{Digest, FixedOutputReset};
6use pkcs8::{
7    spki::{
8        der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef,
9        AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier,
10    },
11    EncodePrivateKey, SecretDocument,
12};
13use rand_core::CryptoRngCore;
14use signature::{
15    hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner,
16};
17use zeroize::ZeroizeOnDrop;
18
19/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
20/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
21#[derive(Debug, Clone)]
22pub struct BlindedSigningKey<D>
23where
24    D: Digest,
25{
26    inner: RsaPrivateKey,
27    salt_len: usize,
28    phantom: PhantomData<D>,
29}
30
31impl<D> BlindedSigningKey<D>
32where
33    D: Digest,
34{
35    /// Create a new RSASSA-PSS signing key which produces "blinded"
36    /// signatures.
37    /// Digest output size is used as a salt length.
38    pub fn new(key: RsaPrivateKey) -> Self {
39        Self::new_with_salt_len(key, <D as Digest>::output_size())
40    }
41
42    /// Create a new RSASSA-PSS signing key which produces "blinded"
43    /// signatures with a salt of the given length.
44    pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
45        Self {
46            inner: key,
47            salt_len,
48            phantom: Default::default(),
49        }
50    }
51
52    /// Create a new random RSASSA-PSS signing key which produces "blinded"
53    /// signatures.
54    /// Digest output size is used as a salt length.
55    pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
56        Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
57    }
58
59    /// Create a new random RSASSA-PSS signing key which produces "blinded"
60    /// signatures with a salt of the given length.
61    pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
62        rng: &mut R,
63        bit_size: usize,
64        salt_len: usize,
65    ) -> Result<Self> {
66        Ok(Self {
67            inner: RsaPrivateKey::new(rng, bit_size)?,
68            salt_len,
69            phantom: Default::default(),
70        })
71    }
72
73    /// Return specified salt length for this key
74    pub fn salt_len(&self) -> usize {
75        self.salt_len
76    }
77}
78
79//
80// `*Signer` trait impls
81//
82
83impl<D> RandomizedSigner<Signature> for BlindedSigningKey<D>
84where
85    D: Digest + FixedOutputReset,
86{
87    fn try_sign_with_rng(
88        &self,
89        rng: &mut impl CryptoRngCore,
90        msg: &[u8],
91    ) -> signature::Result<Signature> {
92        sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)?
93            .as_slice()
94            .try_into()
95    }
96}
97
98impl<D> RandomizedDigestSigner<D, Signature> for BlindedSigningKey<D>
99where
100    D: Digest + FixedOutputReset,
101{
102    fn try_sign_digest_with_rng(
103        &self,
104        rng: &mut impl CryptoRngCore,
105        digest: D,
106    ) -> signature::Result<Signature> {
107        sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)?
108            .as_slice()
109            .try_into()
110    }
111}
112
113impl<D> RandomizedPrehashSigner<Signature> for BlindedSigningKey<D>
114where
115    D: Digest + FixedOutputReset,
116{
117    fn sign_prehash_with_rng(
118        &self,
119        rng: &mut impl CryptoRngCore,
120        prehash: &[u8],
121    ) -> signature::Result<Signature> {
122        sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)?
123            .as_slice()
124            .try_into()
125    }
126}
127
128//
129// Other trait impls
130//
131
132impl<D> AsRef<RsaPrivateKey> for BlindedSigningKey<D>
133where
134    D: Digest,
135{
136    fn as_ref(&self) -> &RsaPrivateKey {
137        &self.inner
138    }
139}
140
141impl<D> AssociatedAlgorithmIdentifier for BlindedSigningKey<D>
142where
143    D: Digest,
144{
145    type Params = AnyRef<'static>;
146
147    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
148}
149
150impl<D> DynSignatureAlgorithmIdentifier for BlindedSigningKey<D>
151where
152    D: Digest + AssociatedOid,
153{
154    fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
155        get_pss_signature_algo_id::<D>(self.salt_len as u8)
156    }
157}
158
159impl<D> EncodePrivateKey for BlindedSigningKey<D>
160where
161    D: Digest,
162{
163    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
164        self.inner.to_pkcs8_der()
165    }
166}
167
168impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
169where
170    D: Digest,
171{
172    fn from(key: RsaPrivateKey) -> Self {
173        Self::new(key)
174    }
175}
176
177impl<D> From<BlindedSigningKey<D>> for RsaPrivateKey
178where
179    D: Digest,
180{
181    fn from(key: BlindedSigningKey<D>) -> Self {
182        key.inner
183    }
184}
185
186impl<D> Keypair for BlindedSigningKey<D>
187where
188    D: Digest,
189{
190    type VerifyingKey = VerifyingKey<D>;
191    fn verifying_key(&self) -> Self::VerifyingKey {
192        VerifyingKey {
193            inner: self.inner.to_public_key(),
194            salt_len: self.salt_len,
195            phantom: Default::default(),
196        }
197    }
198}
199
200impl<D> ZeroizeOnDrop for BlindedSigningKey<D> where D: Digest {}