Skip to content

Commit c98e124

Browse files
committed
Move Mpint from ssh-key to ssh-encoding
This makes it possible to support the `mpint` data type encoding in other applications than just `ssh-key`.
1 parent 2b55b09 commit c98e124

File tree

8 files changed

+58
-33
lines changed

8 files changed

+58
-33
lines changed

Cargo.lock

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ssh-encoding/Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,20 @@ bytes = { version = "1", optional = true, default-features = false }
2121
digest = { version = "=0.11.0-pre.9", optional = true, default-features = false }
2222
pem-rfc7468 = { version = "1.0.0-rc.2", optional = true }
2323
ssh-derive = { version = "0.0.1-alpha", optional = true, path = "../ssh-derive" }
24+
subtle = { version = "2", optional = true, default-features = false }
25+
zeroize = { version = "1", optional = true, default-features = false }
26+
27+
# TODO(tarcieri): migrate to `crypto-bigint`
28+
bigint = { package = "num-bigint-dig", version = "0.8", optional = true, default-features = false }
2429

2530
[dev-dependencies]
2631
hex-literal = "1"
2732

2833
[features]
29-
alloc = ["base64ct?/alloc", "pem-rfc7468?/alloc"]
34+
alloc = ["base64ct?/alloc", "pem-rfc7468?/alloc", "zeroize?/alloc"]
3035

3136
base64 = ["dep:base64ct"]
37+
bigint = ["alloc", "zeroize", "dep:bigint"]
3238
bytes = ["alloc", "dep:bytes"]
3339
pem = ["base64", "dep:pem-rfc7468"]
3440
derive = ["ssh-derive"]

ssh-encoding/src/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ pub enum Error {
2323
/// Invalid length.
2424
Length,
2525

26+
/// `mpint` encoding errors.
27+
#[cfg(feature = "alloc")]
28+
MpintEncoding,
29+
2630
/// Overflow errors.
2731
Overflow,
2832

@@ -60,6 +64,8 @@ impl fmt::Display for Error {
6064
Error::CharacterEncoding => write!(f, "character encoding invalid"),
6165
Error::Label(err) => write!(f, "{}", err),
6266
Error::Length => write!(f, "length invalid"),
67+
#[cfg(feature = "alloc")]
68+
Error::MpintEncoding => write!(f, "`mpint` encoding invalid"),
6369
Error::Overflow => write!(f, "internal overflow error"),
6470
#[cfg(feature = "pem")]
6571
Error::Pem(err) => write!(f, "{err}"),

ssh-encoding/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
//! The UTF-8 mapping does not alter the encoding of US-ASCII characters.
8484
//!
8585
//! ### `mpint`: multiple precision integers in two's complement format
86+
//! #### [`Decode`]/[`Encode`] trait impls: `Mpint`
8687
//!
8788
//! Stored as a byte string, 8 bits per byte, MSB first (a.k.a. big endian).
8889
//!
@@ -214,6 +215,8 @@ mod decode;
214215
mod encode;
215216
mod error;
216217
mod label;
218+
#[cfg(feature = "alloc")]
219+
mod mpint;
217220
#[macro_use]
218221
mod reader;
219222
mod writer;
@@ -233,6 +236,9 @@ pub use crate::{
233236
writer::Writer,
234237
};
235238

239+
#[cfg(feature = "alloc")]
240+
pub use crate::mpint::Mpint;
241+
236242
#[cfg(feature = "base64")]
237243
pub use crate::{base64::Base64Reader, base64::Base64Writer};
238244

ssh-key/src/mpint.rs renamed to ssh-encoding/src/mpint.rs

+22-18
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
//! Multiple precision integer
22
3-
use crate::{Error, Result};
3+
use crate::{CheckedSum, Decode, Encode, Error, Reader, Result, Writer};
44
use alloc::{boxed::Box, vec::Vec};
55
use core::fmt;
6-
use encoding::{CheckedSum, Decode, Encode, Reader, Writer};
6+
7+
#[cfg(feature = "subtle")]
78
use subtle::{Choice, ConstantTimeEq};
8-
use zeroize::Zeroize;
99

10-
#[cfg(any(feature = "dsa", feature = "rsa"))]
10+
#[cfg(any(feature = "bigint", feature = "zeroize"))]
11+
use zeroize::Zeroize;
12+
#[cfg(feature = "bigint")]
1113
use zeroize::Zeroizing;
1214

13-
/// Multiple precision integer, a.k.a. "mpint".
14-
///
15-
/// This type is used for representing the big integer components of
16-
/// DSA and RSA keys.
15+
/// Multiple precision integer, a.k.a. `mpint`.
1716
///
1817
/// Described in [RFC4251 § 5](https://datatracker.ietf.org/doc/html/rfc4251#section-5):
1918
///
@@ -38,7 +37,8 @@ use zeroize::Zeroizing;
3837
/// | 80 | `00 00 00 02 00 80`
3938
/// |-1234 | `00 00 00 02 ed cc`
4039
/// | -deadbeef | `00 00 00 05 ff 21 52 41 11`
41-
#[derive(Clone, PartialOrd, Ord)]
40+
#[cfg_attr(not(feature = "subtle"), derive(Clone))]
41+
#[cfg_attr(feature = "subtle", derive(Clone, Ord, PartialOrd))] // TODO: constant time (Partial)`Ord`?
4242
pub struct Mpint {
4343
/// Inner big endian-serialized integer value
4444
inner: Box<[u8]>,
@@ -109,14 +109,17 @@ impl AsRef<[u8]> for Mpint {
109109
}
110110
}
111111

112+
#[cfg(feature = "subtle")]
112113
impl ConstantTimeEq for Mpint {
113114
fn ct_eq(&self, other: &Self) -> Choice {
114115
self.as_ref().ct_eq(other.as_ref())
115116
}
116117
}
117118

119+
#[cfg(feature = "subtle")]
118120
impl Eq for Mpint {}
119121

122+
#[cfg(feature = "subtle")]
120123
impl PartialEq for Mpint {
121124
fn eq(&self, other: &Self) -> bool {
122125
self.ct_eq(other).into()
@@ -132,11 +135,11 @@ impl Decode for Mpint {
132135
}
133136

134137
impl Encode for Mpint {
135-
fn encoded_len(&self) -> encoding::Result<usize> {
138+
fn encoded_len(&self) -> Result<usize> {
136139
[4, self.as_bytes().len()].checked_sum()
137140
}
138141

139-
fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
142+
fn encode(&self, writer: &mut impl Writer) -> Result<()> {
140143
self.as_bytes().encode(writer)?;
141144
Ok(())
142145
}
@@ -156,14 +159,15 @@ impl TryFrom<Box<[u8]>> for Mpint {
156159
fn try_from(bytes: Box<[u8]>) -> Result<Self> {
157160
match &*bytes {
158161
// Unnecessary leading 0
159-
[0x00] => Err(Error::FormatEncoding),
162+
[0x00] => Err(Error::MpintEncoding),
160163
// Unnecessary leading 0
161-
[0x00, n, ..] if *n < 0x80 => Err(Error::FormatEncoding),
164+
[0x00, n, ..] if *n < 0x80 => Err(Error::MpintEncoding),
162165
_ => Ok(Self { inner: bytes }),
163166
}
164167
}
165168
}
166169

170+
#[cfg(feature = "zeroize")]
167171
impl Zeroize for Mpint {
168172
fn zeroize(&mut self) {
169173
self.inner.zeroize();
@@ -200,7 +204,7 @@ impl fmt::UpperHex for Mpint {
200204
}
201205
}
202206

203-
#[cfg(any(feature = "dsa", feature = "rsa"))]
207+
#[cfg(feature = "bigint")]
204208
impl TryFrom<bigint::BigUint> for Mpint {
205209
type Error = Error;
206210

@@ -209,7 +213,7 @@ impl TryFrom<bigint::BigUint> for Mpint {
209213
}
210214
}
211215

212-
#[cfg(any(feature = "dsa", feature = "rsa"))]
216+
#[cfg(feature = "bigint")]
213217
impl TryFrom<&bigint::BigUint> for Mpint {
214218
type Error = Error;
215219

@@ -219,7 +223,7 @@ impl TryFrom<&bigint::BigUint> for Mpint {
219223
}
220224
}
221225

222-
#[cfg(any(feature = "dsa", feature = "rsa"))]
226+
#[cfg(feature = "bigint")]
223227
impl TryFrom<Mpint> for bigint::BigUint {
224228
type Error = Error;
225229

@@ -228,15 +232,15 @@ impl TryFrom<Mpint> for bigint::BigUint {
228232
}
229233
}
230234

231-
#[cfg(any(feature = "dsa", feature = "rsa"))]
235+
#[cfg(feature = "bigint")]
232236
impl TryFrom<&Mpint> for bigint::BigUint {
233237
type Error = Error;
234238

235239
fn try_from(mpint: &Mpint) -> Result<bigint::BigUint> {
236240
mpint
237241
.as_positive_bytes()
238242
.map(bigint::BigUint::from_bytes_be)
239-
.ok_or(Error::Crypto)
243+
.ok_or(Error::MpintEncoding)
240244
}
241245
}
242246

ssh-key/Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ rust-version = "1.85"
1919

2020
[dependencies]
2121
cipher = { package = "ssh-cipher", version = "=0.3.0-pre.2", features = ["zeroize"], path = "../ssh-cipher" }
22-
encoding = { package = "ssh-encoding", version = "=0.3.0-pre.1", features = ["base64", "digest", "pem"], path = "../ssh-encoding" }
22+
encoding = { package = "ssh-encoding", version = "=0.3.0-pre.1", features = ["base64", "digest", "pem", "subtle", "zeroize"], path = "../ssh-encoding" }
2323
sha2 = { version = "=0.11.0-pre.4", default-features = false }
2424
signature = { version = "=2.3.0-pre.4", default-features = false }
2525
subtle = { version = "2", default-features = false }
@@ -66,7 +66,7 @@ std = [
6666
]
6767

6868
crypto = ["ed25519", "p256", "p384", "p521", "rsa"] # NOTE: `dsa` is obsolete/weak
69-
dsa = ["dep:bigint", "dep:dsa", "dep:sha1", "alloc", "signature/rand_core"]
69+
dsa = ["dep:bigint", "dep:dsa", "dep:sha1", "alloc", "encoding/bigint", "signature/rand_core"]
7070
ecdsa = ["dep:sec1"]
7171
ed25519 = ["dep:ed25519-dalek", "rand_core"]
7272
encryption = [
@@ -83,7 +83,7 @@ p256 = ["dep:p256", "ecdsa"]
8383
p384 = ["dep:p384", "ecdsa"]
8484
p521 = ["dep:p521", "ecdsa"]
8585
ppk = ["dep:hex", "alloc", "cipher/aes-cbc", "dep:hmac", "dep:argon2", "dep:sha1"]
86-
rsa = ["dep:bigint", "dep:rsa", "alloc", "rand_core"]
86+
rsa = ["dep:bigint", "dep:rsa", "alloc", "encoding/bigint", "rand_core"]
8787
sha1 = ["dep:sha1"]
8888
tdes = ["cipher/tdes", "encryption"]
8989

ssh-key/src/lib.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@ mod kdf;
158158

159159
#[cfg(feature = "std")]
160160
mod dot_ssh;
161-
#[cfg(feature = "alloc")]
162-
mod mpint;
163161
#[cfg(feature = "ppk")]
164162
mod ppk;
165163
#[cfg(feature = "alloc")]
@@ -181,13 +179,15 @@ pub use encoding::pem::LineEnding;
181179
pub use sha2;
182180

183181
#[cfg(feature = "alloc")]
184-
pub use crate::{
185-
algorithm::AlgorithmName,
186-
certificate::Certificate,
187-
known_hosts::KnownHosts,
188-
mpint::Mpint,
189-
signature::{Signature, SigningKey},
190-
sshsig::SshSig,
182+
pub use {
183+
crate::{
184+
algorithm::AlgorithmName,
185+
certificate::Certificate,
186+
known_hosts::KnownHosts,
187+
signature::{Signature, SigningKey},
188+
sshsig::SshSig,
189+
},
190+
encoding::Mpint,
191191
};
192192

193193
#[cfg(feature = "ecdsa")]

ssh-key/src/private/dsa.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl TryFrom<DsaPrivateKey> for dsa::BigUint {
9797
type Error = Error;
9898

9999
fn try_from(key: DsaPrivateKey) -> Result<dsa::BigUint> {
100-
dsa::BigUint::try_from(&key.inner)
100+
Ok(dsa::BigUint::try_from(&key.inner)?)
101101
}
102102
}
103103

@@ -106,7 +106,7 @@ impl TryFrom<&DsaPrivateKey> for dsa::BigUint {
106106
type Error = Error;
107107

108108
fn try_from(key: &DsaPrivateKey) -> Result<dsa::BigUint> {
109-
dsa::BigUint::try_from(&key.inner)
109+
Ok(dsa::BigUint::try_from(&key.inner)?)
110110
}
111111
}
112112

0 commit comments

Comments
 (0)