Skip to content

Commit 2ad1356

Browse files
committed
ssh-key: enable and fix workspace-level lints
Applies the workspace-level config added in #509 to this crate and fixes any failures.
1 parent cee1d81 commit 2ad1356

44 files changed

Lines changed: 872 additions & 365 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ssh-key/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ rsa = ["dep:rsa", "alloc", "encoding/bigint", "rand_core"]
8181
sha1 = ["dep:sha1"]
8282
tdes = ["cipher/tdes", "encryption"]
8383

84+
[lints]
85+
workspace = true
86+
8487
[package.metadata.docs.rs]
8588
all-features = true
86-
rustdoc-args = ["--cfg", "docsrs"]

ssh-key/README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,6 @@ The "Feature" column lists the name of `ssh-key` crate features which can
8484
be enabled to provide full support for the "Keygen", "Sign", and "Verify"
8585
functionality for a particular SSH key algorithm.
8686

87-
## Minimum Supported Rust Version
88-
89-
This crate requires **Rust 1.85** at a minimum.
90-
91-
We may change the MSRV in the future, but it will be accompanied by a minor
92-
version bump.
93-
9487
## License
9588

9689
Licensed under either of:

ssh-key/src/algorithm.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,14 @@ impl Algorithm {
140140
/// - `sk-ssh-ed25519@openssh.com` (FIDO/U2F key)
141141
///
142142
/// Any other algorithms are mapped to the [`Algorithm::Other`] variant.
143+
///
144+
/// # Errors
145+
/// Returns [`Error::Encoding`] in the event the algorithm name is not known.
143146
pub fn new(id: &str) -> Result<Self> {
144147
Ok(id.parse()?)
145148
}
146149

147-
/// Decode algorithm from the given string identifier as used by
148-
/// the OpenSSH certificate format.
150+
/// Decode algorithm from the given string identifier as used by the OpenSSH certificate format.
149151
///
150152
/// OpenSSH certificate algorithms end in `*-cert-v01@openssh.com`.
151153
/// See [PROTOCOL.certkeys] for more information.
@@ -163,6 +165,9 @@ impl Algorithm {
163165
/// Any other algorithms are mapped to the [`Algorithm::Other`] variant.
164166
///
165167
/// [PROTOCOL.certkeys]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
168+
///
169+
/// # Errors
170+
/// Returns [`Error::AlgorithmUnknown`] in the event the algorithm is not known.
166171
pub fn new_certificate(id: &str) -> Result<Self> {
167172
match id {
168173
CERT_DSA => Ok(Algorithm::Dsa),
@@ -193,6 +198,7 @@ impl Algorithm {
193198
}
194199

195200
/// Get the string identifier which corresponds to this algorithm.
201+
#[must_use]
196202
pub fn as_str(&self) -> &str {
197203
match self {
198204
Algorithm::Dsa => SSH_DSA,
@@ -222,6 +228,7 @@ impl Algorithm {
222228
///
223229
/// [PROTOCOL.certkeys]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
224230
#[cfg(feature = "alloc")]
231+
#[must_use]
225232
pub fn to_certificate_type(&self) -> String {
226233
match self {
227234
Algorithm::Dsa => CERT_DSA,
@@ -246,21 +253,25 @@ impl Algorithm {
246253
}
247254

248255
/// Is the algorithm DSA?
256+
#[must_use]
249257
pub fn is_dsa(self) -> bool {
250258
self == Algorithm::Dsa
251259
}
252260

253261
/// Is the algorithm ECDSA?
262+
#[must_use]
254263
pub fn is_ecdsa(self) -> bool {
255264
matches!(self, Algorithm::Ecdsa { .. })
256265
}
257266

258267
/// Is the algorithm Ed25519?
268+
#[must_use]
259269
pub fn is_ed25519(self) -> bool {
260270
self == Algorithm::Ed25519
261271
}
262272

263273
/// Is the algorithm RSA?
274+
#[must_use]
264275
pub fn is_rsa(self) -> bool {
265276
matches!(self, Algorithm::Rsa { .. })
266277
}
@@ -340,11 +351,15 @@ impl EcdsaCurve {
340351
/// - `nistp256`
341352
/// - `nistp384`
342353
/// - `nistp521`
354+
///
355+
/// # Errors
356+
/// Returns [`Error::Encoding`] in the event the algorithm name is not known.
343357
pub fn new(id: &str) -> Result<Self> {
344358
Ok(id.parse()?)
345359
}
346360

347361
/// Get the string identifier which corresponds to this ECDSA elliptic curve.
362+
#[must_use]
348363
pub fn as_str(self) -> &'static str {
349364
match self {
350365
EcdsaCurve::NistP256 => "nistp256",
@@ -370,6 +385,12 @@ impl AsRef<str> for EcdsaCurve {
370385
}
371386
}
372387

388+
impl From<EcdsaCurve> for Algorithm {
389+
fn from(curve: EcdsaCurve) -> Algorithm {
390+
Algorithm::Ecdsa { curve }
391+
}
392+
}
393+
373394
impl Label for EcdsaCurve {}
374395

375396
impl fmt::Display for EcdsaCurve {
@@ -410,11 +431,15 @@ impl HashAlg {
410431
///
411432
/// - `sha256`
412433
/// - `sha512`
434+
///
435+
/// # Errors
436+
/// Returns [`Error::Encoding`] in the event the algorithm name is not known.
413437
pub fn new(id: &str) -> Result<Self> {
414438
Ok(id.parse()?)
415439
}
416440

417441
/// Get the string identifier for this hash algorithm.
442+
#[must_use]
418443
pub fn as_str(self) -> &'static str {
419444
match self {
420445
HashAlg::Sha256 => SHA256,
@@ -423,6 +448,7 @@ impl HashAlg {
423448
}
424449

425450
/// Get the size of a digest produced by this hash function.
451+
#[must_use]
426452
pub const fn digest_size(self) -> usize {
427453
match self {
428454
HashAlg::Sha256 => 32,
@@ -432,6 +458,7 @@ impl HashAlg {
432458

433459
/// Compute a digest of the given message using this hash function.
434460
#[cfg(feature = "alloc")]
461+
#[must_use]
435462
pub fn digest(self, msg: &[u8]) -> Vec<u8> {
436463
match self {
437464
HashAlg::Sha256 => Sha256::digest(msg).to_vec(),
@@ -497,11 +524,16 @@ impl KdfAlg {
497524
///
498525
/// # Supported KDF names
499526
/// - `none`
527+
/// - `bcrypt`
528+
///
529+
/// # Errors
530+
/// Returns [`Error::Encoding`] in the event the algorithm name is not known.
500531
pub fn new(kdfname: &str) -> Result<Self> {
501532
Ok(kdfname.parse()?)
502533
}
503534

504535
/// Get the string identifier which corresponds to this algorithm.
536+
#[must_use]
505537
pub fn as_str(self) -> &'static str {
506538
match self {
507539
Self::None => NONE,
@@ -510,6 +542,7 @@ impl KdfAlg {
510542
}
511543

512544
/// Is the KDF algorithm "none"?
545+
#[must_use]
513546
pub fn is_none(self) -> bool {
514547
self == Self::None
515548
}

ssh-key/src/algorithm/name.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ pub struct AlgorithmName {
3737

3838
impl AlgorithmName {
3939
/// Create a new algorithm identifier.
40+
///
41+
/// # Errors
42+
/// Returns [`LabelError`] in the event the identifier is invalid.
4043
pub fn new(id: impl Into<String>) -> Result<Self, LabelError> {
4144
let id = id.into();
4245
validate_algorithm_id(&id, MAX_ALGORITHM_NAME_LEN)?;
@@ -45,17 +48,23 @@ impl AlgorithmName {
4548
}
4649

4750
/// Get the string identifier which corresponds to this algorithm name.
51+
#[must_use]
4852
pub fn as_str(&self) -> &str {
4953
&self.id
5054
}
5155

5256
/// Get the string identifier which corresponds to the OpenSSH certificate format.
57+
#[must_use]
58+
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
5359
pub fn certificate_type(&self) -> String {
5460
let (name, domain) = split_algorithm_id(&self.id).expect("format checked in constructor");
5561
format!("{name}{CERT_STR_SUFFIX}@{domain}")
5662
}
5763

5864
/// Create a new [`AlgorithmName`] from an OpenSSH certificate format string identifier.
65+
///
66+
/// # Errors
67+
/// Returns [`LabelError`] in the event the identifier is invalid.
5968
pub fn from_certificate_type(id: &str) -> Result<Self, LabelError> {
6069
validate_algorithm_id(id, MAX_CERT_STR_LEN)?;
6170

@@ -65,9 +74,9 @@ impl AlgorithmName {
6574
.strip_suffix(CERT_STR_SUFFIX)
6675
.ok_or_else(|| LabelError::new(id))?;
6776

68-
let algorithm_name = format!("{name}@{domain}");
69-
70-
Ok(Self { id: algorithm_name })
77+
Ok(Self {
78+
id: format!("{name}@{domain}"),
79+
})
7180
}
7281
}
7382

ssh-key/src/authorized_keys.rs

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
//! Parser for `AuthorizedKeysFile`-formatted data.
22
33
use crate::{Error, PublicKey, Result};
4-
use core::str;
4+
use core::{
5+
fmt::{self, Debug},
6+
str,
7+
};
58

69
#[cfg(feature = "alloc")]
7-
use {
8-
alloc::string::{String, ToString},
9-
core::fmt,
10-
};
10+
use alloc::string::{String, ToString};
1111

1212
#[cfg(feature = "std")]
1313
use {
@@ -44,24 +44,27 @@ pub struct AuthorizedKeys<'a> {
4444

4545
impl<'a> AuthorizedKeys<'a> {
4646
/// Create a new parser for the given input buffer.
47+
#[must_use]
4748
pub fn new(input: &'a str) -> Self {
4849
Self {
4950
lines: input.lines(),
5051
}
5152
}
5253

53-
/// Read an [`AuthorizedKeys`] file from the filesystem, returning an
54-
/// [`Entry`] vector on success.
54+
/// Read an [`AuthorizedKeys`] file from the filesystem, returning an [`Entry`] vector on
55+
/// success.
56+
///
57+
/// # Errors
58+
/// - Returns [`Error::Io`] in event of I/O errors reading the file.
59+
/// - Propagates [`Entry`] parsing errors as [`Error::FormatEncoding`].
5560
#[cfg(feature = "std")]
5661
pub fn read_file(path: impl AsRef<Path>) -> Result<Vec<Entry>> {
5762
// TODO(tarcieri): permissions checks
5863
let input = fs::read_to_string(path)?;
5964
AuthorizedKeys::new(&input).collect()
6065
}
6166

62-
/// Get the next line, trimming any comments and trailing whitespace.
63-
///
64-
/// Ignores empty lines.
67+
/// Get the next line, trimming any comments and trailing whitespace. Ignores empty lines.
6568
fn next_line_trimmed(&mut self) -> Option<&'a str> {
6669
loop {
6770
let mut line = self.lines.next()?;
@@ -81,11 +84,17 @@ impl<'a> AuthorizedKeys<'a> {
8184
}
8285
}
8386

87+
impl Debug for AuthorizedKeys<'_> {
88+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89+
f.debug_struct("AuthorizedKeys").finish_non_exhaustive()
90+
}
91+
}
92+
8493
impl Iterator for AuthorizedKeys<'_> {
8594
type Item = Result<Entry>;
8695

8796
fn next(&mut self) -> Option<Result<Entry>> {
88-
self.next_line_trimmed().map(|line| line.parse())
97+
self.next_line_trimmed().map(str::parse)
8998
}
9099
}
91100

@@ -103,11 +112,13 @@ pub struct Entry {
103112
impl Entry {
104113
/// Get configuration options for this entry.
105114
#[cfg(feature = "alloc")]
115+
#[must_use]
106116
pub fn config_opts(&self) -> &ConfigOpts {
107117
&self.config_opts
108118
}
109119

110120
/// Get public key for this entry.
121+
#[must_use]
111122
pub fn public_key(&self) -> &PublicKey {
112123
&self.public_key
113124
}
@@ -206,23 +217,29 @@ pub struct ConfigOpts(String);
206217
#[cfg(feature = "alloc")]
207218
impl ConfigOpts {
208219
/// Parse an options string.
220+
///
221+
/// # Errors
222+
///
209223
pub fn new(string: impl Into<String>) -> Result<Self> {
210224
let ret = Self(string.into());
211225
ret.iter().validate()?;
212226
Ok(ret)
213227
}
214228

215229
/// Borrow the configuration options as a `str`.
230+
#[must_use]
216231
pub fn as_str(&self) -> &str {
217232
self.0.as_str()
218233
}
219234

220235
/// Are there no configuration options?
236+
#[must_use]
221237
pub fn is_empty(&self) -> bool {
222238
self.0.is_empty()
223239
}
224240

225241
/// Iterate over the comma-delimited configuration options.
242+
#[must_use]
226243
pub fn iter(&self) -> ConfigOptsIter<'_> {
227244
ConfigOptsIter(self.as_str())
228245
}
@@ -259,6 +276,9 @@ impl<'a> ConfigOptsIter<'a> {
259276
/// Create new configuration options iterator.
260277
///
261278
/// Validates that the options are well-formed.
279+
///
280+
/// # Errors
281+
/// Returns [`Error::Encoding`] in the event of encoding errors.
262282
pub fn new(s: &'a str) -> Result<Self> {
263283
let ret = Self(s);
264284
ret.clone().validate()?;

0 commit comments

Comments
 (0)