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