Skip to main content

ssh_key/private/
dsa.rs

1//! Digital Signature Algorithm (DSA) private keys.
2
3use crate::{Error, Mpint, Result, public::DsaPublicKey};
4use core::fmt;
5use ctutils::{Choice, CtEq};
6use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
7use zeroize::Zeroize;
8
9#[cfg(feature = "dsa")]
10use encoding::Uint;
11#[cfg(all(feature = "dsa", feature = "rand_core"))]
12use rand_core::CryptoRng;
13
14/// Digital Signature Algorithm (DSA) private key.
15///
16/// Uniformly random integer `x`, such that `0 < x < q`, i.e. `x` is in the
17/// range `[1, q–1]`.
18///
19/// Described in [FIPS 186-4 § 4.1](https://csrc.nist.gov/publications/detail/fips/186/4/final).
20#[derive(Clone)]
21pub struct DsaPrivateKey {
22    /// Integer representing a DSA private key.
23    inner: Mpint,
24}
25
26impl DsaPrivateKey {
27    /// Create a new DSA private key given the value `x`.
28    ///
29    /// # Errors
30    /// Returns [`Error::FormatEncoding`] if `x` is negative.
31    pub fn new(x: Mpint) -> Result<Self> {
32        if x.is_positive() {
33            Ok(Self { inner: x })
34        } else {
35            Err(Error::FormatEncoding)
36        }
37    }
38
39    /// Get the serialized private key as bytes.
40    #[must_use]
41    pub fn as_bytes(&self) -> &[u8] {
42        self.inner.as_bytes()
43    }
44
45    /// Get the inner [`Mpint`].
46    #[must_use]
47    pub fn as_mpint(&self) -> &Mpint {
48        &self.inner
49    }
50}
51
52impl AsRef<[u8]> for DsaPrivateKey {
53    fn as_ref(&self) -> &[u8] {
54        self.as_bytes()
55    }
56}
57
58impl CtEq for DsaPrivateKey {
59    fn ct_eq(&self, other: &Self) -> Choice {
60        self.inner.ct_eq(&other.inner)
61    }
62}
63
64impl Eq for DsaPrivateKey {}
65
66impl PartialEq for DsaPrivateKey {
67    fn eq(&self, other: &Self) -> bool {
68        self.ct_eq(other).into()
69    }
70}
71
72impl TryFrom<Mpint> for DsaPrivateKey {
73    type Error = Error;
74
75    fn try_from(x: Mpint) -> Result<Self> {
76        Self::new(x)
77    }
78}
79
80impl Decode for DsaPrivateKey {
81    type Error = Error;
82
83    fn decode(reader: &mut impl Reader) -> Result<Self> {
84        Self::new(Mpint::decode(reader)?)
85    }
86}
87
88impl Encode for DsaPrivateKey {
89    fn encoded_len(&self) -> encoding::Result<usize> {
90        self.inner.encoded_len()
91    }
92
93    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
94        self.inner.encode(writer)
95    }
96}
97
98impl fmt::Debug for DsaPrivateKey {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        f.debug_struct("DsaPrivateKey").finish_non_exhaustive()
101    }
102}
103
104impl Drop for DsaPrivateKey {
105    fn drop(&mut self) {
106        self.inner.zeroize();
107    }
108}
109
110#[cfg(feature = "dsa")]
111impl From<&dsa::SigningKey> for DsaPrivateKey {
112    fn from(key: &dsa::SigningKey) -> DsaPrivateKey {
113        DsaPrivateKey {
114            inner: key.x().as_ref().into(),
115        }
116    }
117}
118
119#[cfg(feature = "dsa")]
120impl From<dsa::SigningKey> for DsaPrivateKey {
121    fn from(key: dsa::SigningKey) -> DsaPrivateKey {
122        DsaPrivateKey::from(&key)
123    }
124}
125
126#[cfg(feature = "dsa")]
127impl TryFrom<DsaPrivateKey> for Uint {
128    type Error = Error;
129
130    fn try_from(key: DsaPrivateKey) -> Result<Uint> {
131        Ok(Uint::try_from(&key.inner)?)
132    }
133}
134
135#[cfg(feature = "dsa")]
136impl TryFrom<&DsaPrivateKey> for Uint {
137    type Error = Error;
138
139    fn try_from(key: &DsaPrivateKey) -> Result<Uint> {
140        Ok(Uint::try_from(&key.inner)?)
141    }
142}
143
144/// Digital Signature Algorithm (DSA) private/public keypair.
145#[derive(Clone)]
146pub struct DsaKeypair {
147    /// Public key.
148    public: DsaPublicKey,
149
150    /// Private key.
151    private: DsaPrivateKey,
152}
153
154impl DsaKeypair {
155    /// Key size.
156    #[cfg(all(feature = "dsa", feature = "rand_core"))]
157    #[allow(deprecated)]
158    pub(crate) const KEY_SIZE: dsa::KeySize = dsa::KeySize::DSA_1024_160;
159
160    /// Generate a random DSA private key.
161    #[cfg(all(feature = "dsa", feature = "rand_core"))]
162    #[expect(clippy::missing_errors_doc, reason = "TODO")]
163    pub fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Result<Self> {
164        let Ok(components) =
165            dsa::Components::try_generate_from_rng_with_key_size(rng, Self::KEY_SIZE);
166        let Ok(sk) = dsa::SigningKey::try_generate_from_rng_with_components(rng, components);
167        Ok(sk.into())
168    }
169
170    /// Create a new [`DsaKeypair`] with the given `public` and `private` components.
171    ///
172    /// # Errors
173    /// Returns [`Error::Crypto`] if the `public` key does not match the `private` key (TODO).
174    pub fn new(public: DsaPublicKey, private: DsaPrivateKey) -> Result<Self> {
175        // TODO(tarcieri): actually validate the `public` and `private` components match
176        Ok(Self { public, private })
177    }
178
179    /// Get the public component of this key.
180    #[must_use]
181    pub fn public(&self) -> &DsaPublicKey {
182        &self.public
183    }
184
185    /// Get the private component of this key.
186    #[must_use]
187    pub fn private(&self) -> &DsaPrivateKey {
188        &self.private
189    }
190}
191
192impl CtEq for DsaKeypair {
193    fn ct_eq(&self, other: &Self) -> Choice {
194        Choice::from(u8::from(self.public == other.public)) & self.private.ct_eq(&other.private)
195    }
196}
197
198impl PartialEq for DsaKeypair {
199    fn eq(&self, other: &Self) -> bool {
200        self.ct_eq(other).into()
201    }
202}
203
204impl Eq for DsaKeypair {}
205
206impl Decode for DsaKeypair {
207    type Error = Error;
208
209    fn decode(reader: &mut impl Reader) -> Result<Self> {
210        let public = DsaPublicKey::decode(reader)?;
211        let private = DsaPrivateKey::decode(reader)?;
212        DsaKeypair::new(public, private)
213    }
214}
215
216impl Encode for DsaKeypair {
217    fn encoded_len(&self) -> encoding::Result<usize> {
218        [self.public.encoded_len()?, self.private.encoded_len()?].checked_sum()
219    }
220
221    fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
222        self.public.encode(writer)?;
223        self.private.encode(writer)
224    }
225}
226
227impl From<DsaKeypair> for DsaPublicKey {
228    fn from(keypair: DsaKeypair) -> DsaPublicKey {
229        keypair.public
230    }
231}
232
233impl From<&DsaKeypair> for DsaPublicKey {
234    fn from(keypair: &DsaKeypair) -> DsaPublicKey {
235        keypair.public.clone()
236    }
237}
238
239impl fmt::Debug for DsaKeypair {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        f.debug_struct("DsaKeypair")
242            .field("public", &self.public)
243            .finish_non_exhaustive()
244    }
245}
246
247#[cfg(feature = "dsa")]
248impl TryFrom<DsaKeypair> for dsa::SigningKey {
249    type Error = Error;
250
251    fn try_from(key: DsaKeypair) -> Result<dsa::SigningKey> {
252        dsa::SigningKey::try_from(&key)
253    }
254}
255
256#[cfg(feature = "dsa")]
257impl TryFrom<&DsaKeypair> for dsa::SigningKey {
258    type Error = Error;
259
260    fn try_from(key: &DsaKeypair) -> Result<dsa::SigningKey> {
261        Ok(dsa::SigningKey::from_components(
262            dsa::VerifyingKey::try_from(&key.public)?,
263            key.private.as_mpint().try_into()?,
264        )?)
265    }
266}
267
268#[cfg(feature = "dsa")]
269impl From<dsa::SigningKey> for DsaKeypair {
270    fn from(key: dsa::SigningKey) -> DsaKeypair {
271        DsaKeypair::from(&key)
272    }
273}
274
275#[cfg(feature = "dsa")]
276impl From<&dsa::SigningKey> for DsaKeypair {
277    fn from(key: &dsa::SigningKey) -> DsaKeypair {
278        DsaKeypair {
279            private: key.into(),
280            public: key.verifying_key().into(),
281        }
282    }
283}