diff --git a/Cargo.lock b/Cargo.lock index 149d8382..32ae54e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,7 +473,6 @@ dependencies = [ "der", "digest", "elliptic-curve", - "hex-literal", "rfc6979", "serdect", "sha2", diff --git a/ecdsa/Cargo.toml b/ecdsa/Cargo.toml index eac7fb4a..df4c08a5 100644 --- a/ecdsa/Cargo.toml +++ b/ecdsa/Cargo.toml @@ -31,7 +31,6 @@ spki = { version = "0.8", optional = true, default-features = false } [dev-dependencies] elliptic-curve = { version = "0.14", default-features = false, features = ["dev"] } -hex-literal = "1" sha2 = { version = "0.11", default-features = false } [features] @@ -48,6 +47,8 @@ pkcs8 = ["der", "digest", "elliptic-curve/pkcs8"] pem = ["elliptic-curve/pem", "pkcs8"] serde = ["dep:serdect", "elliptic-curve/serde", "pkcs8"] +[lints] +workspace = true + [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/ecdsa/src/der.rs b/ecdsa/src/der.rs index 3801a813..1f110aa0 100644 --- a/ecdsa/src/der.rs +++ b/ecdsa/src/der.rs @@ -394,6 +394,7 @@ impl<'a> DecodeValue<'a> for SignatureRef<'a> { impl<'a> Sequence<'a> for SignatureRef<'a> {} /// Locate the range within a slice at which a particular subslice is located +#[allow(clippy::as_conversions)] fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result> { let outer_start = outer.as_ptr() as usize; let inner_start = inner.as_ptr() as usize; diff --git a/ecdsa/src/dev.rs b/ecdsa/src/dev.rs index 31cdaedc..f40a7910 100644 --- a/ecdsa/src/dev.rs +++ b/ecdsa/src/dev.rs @@ -282,6 +282,7 @@ impl EcdsaCurve for MockCurve { } /// ECDSA test vector +#[derive(Clone, Copy, Debug)] pub struct TestVector { /// Private scalar pub d: &'static [u8], @@ -306,6 +307,7 @@ pub struct TestVector { } #[cfg(test)] +#[allow(clippy::integer_division_remainder_used, reason = "tests")] mod tests { use super::*; diff --git a/ecdsa/src/hazmat.rs b/ecdsa/src/hazmat.rs index 10826ec0..5d7464b9 100644 --- a/ecdsa/src/hazmat.rs +++ b/ecdsa/src/hazmat.rs @@ -161,7 +161,7 @@ where pub(crate) fn bytes2scalar(mut bytes: &[u8]) -> Scalar { // Compute number of bytes in `n` (curve order) let n_bits = C::ORDER.bits(); - let n_bytes = n_bits.div_ceil(8) as usize; + let n_bytes = usize::try_from(n_bits.div_ceil(8)).expect("overflow"); if bytes.len() > n_bytes { bytes = &bytes[..n_bytes]; } diff --git a/ecdsa/src/lib.rs b/ecdsa/src/lib.rs index 4ecf63af..9e4c8c27 100644 --- a/ecdsa/src/lib.rs +++ b/ecdsa/src/lib.rs @@ -5,47 +5,26 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] -#![forbid(unsafe_code)] -#![warn( - clippy::cast_lossless, - clippy::cast_possible_truncation, - clippy::cast_possible_wrap, - clippy::cast_precision_loss, - clippy::cast_sign_loss, - clippy::checked_conversions, - clippy::implicit_saturating_sub, - clippy::panic, - clippy::panic_in_result_fn, - clippy::unwrap_used, - missing_docs, - rust_2018_idioms, - unused_lifetimes, - unused_qualifications, - unreachable_pub -)] +#![allow(clippy::missing_errors_doc, reason = "TODO")] //! ## `serde` support //! -//! When the `serde` feature of this crate is enabled, `Serialize` and -//! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`] -//! types. +//! When the `serde` feature of this crate is enabled, `Serialize` and `Deserialize` impls are +//! provided for the [`Signature`] and [`VerifyingKey`] types. //! //! Please see type-specific documentation for more information. //! //! ## Interop //! -//! Any crates which provide an implementation of ECDSA for a particular -//! elliptic curve can leverage the types from this crate, along with the -//! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in -//! a generic, interoperable way by leveraging the [`Signature`] type with in -//! conjunction with the [`signature::Signer`] and [`signature::Verifier`] -//! traits. +//! Any crates which provide an implementation of ECDSA for a particular elliptic curve can leverage +//! the types from this crate, along with the [`k256`], [`p256`], and/or [`p384`] crates to expose +//! ECDSA functionality in a generic, interoperable way by leveraging the [`Signature`] type with in +//! conjunction with the [`signature::Signer`] and [`signature::Verifier`] traits. //! -//! For example, the [`ring-compat`] crate implements the [`signature::Signer`] -//! and [`signature::Verifier`] traits in conjunction with the -//! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to -//! wrap the ECDSA implementations from [*ring*] in a generic, interoperable -//! API. +//! For example, the [`ring-compat`] crate implements the [`signature::Signer`] and +//! [`signature::Verifier`] traits in conjunction with the [`p256::ecdsa::Signature`] and +//! [`p384::ecdsa::Signature`] types to wrap the ECDSA implementations from [*ring*] in a generic, +//! interoperable API. //! //! [`k256`]: https://docs.rs/k256 //! [`p256`]: https://docs.rs/p256 @@ -188,21 +167,19 @@ pub type SignatureBytes = Array>; /// /// Both `r` and `s` MUST be non-zero. /// -/// For example, in a curve with a 256-bit modulus like NIST P-256 or -/// secp256k1, `r` and `s` will both be 32-bytes and serialized as big endian, -/// resulting in a signature with a total of 64-bytes. +/// For example, in a curve with a 256-bit modulus like NIST P-256 or secp256k1, `r` and `s` are +/// both 32-bytes and serialized as big endian, resulting in a signature with a total of 64-bytes. /// -/// ASN.1 DER-encoded signatures also supported via the -/// [`Signature::from_der`] and [`Signature::to_der`] methods. +/// ASN.1 DER-encoded signatures also supported via the [`Signature::from_der`] and +/// [`Signature::to_der`] methods. /// /// # `serde` support /// -/// When the `serde` feature of this crate is enabled, it provides support for -/// serializing and deserializing ECDSA signatures using the `Serialize` and -/// `Deserialize` traits. +/// When the `serde` feature of this crate is enabled, it provides support for serializing and +/// deserializing ECDSA signatures using the `Serialize` and `Deserialize` traits. /// -/// The serialization uses a hexadecimal encoding when used with -/// "human readable" text formats, and a binary encoding otherwise. +/// The serialization uses a hexadecimal encoding when used with "human readable" text formats, and +/// a binary encoding otherwise. /// /// [IEEE P1363]: https://en.wikipedia.org/wiki/IEEE_P1363 #[derive(Clone, Copy, Eq, PartialEq)] @@ -218,11 +195,9 @@ where /// Parse a signature from fixed-width bytes, i.e. 2 * the size of /// [`FieldBytes`] for a particular curve. /// - /// # Returns - /// - `Ok(signature)` if the `r` and `s` components are both in the valid - /// range `1..n` when serialized as concatenated big endian integers. - /// - `Err(err)` if the `r` and/or `s` component of the signature is - /// out-of-range when interpreted as a big endian integer. + /// # Errors + /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big + /// endian integer. pub fn from_bytes(bytes: &SignatureBytes) -> Result { let chunks = FieldBytes::::slice_as_chunks(bytes).0; let r = chunks[0]; @@ -250,11 +225,8 @@ where /// Create a [`Signature`] from the serialized `r` and `s` scalar values /// which comprise the signature. /// - /// # Returns - /// - `Ok(signature)` if the `r` and `s` components are both in the valid - /// range `1..n` when serialized as concatenated big endian integers. - /// - `Err(err)` if the `r` and/or `s` component of the signature is - /// out-of-range when interpreted as a big endian integer. + /// # Errors + /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big endian integer. pub fn from_scalars(r: impl Into>, s: impl Into>) -> Result { let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?; let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?; @@ -282,6 +254,7 @@ where /// Serialize this signature as ASN.1 DER. #[cfg(feature = "der")] + #[allow(clippy::missing_panics_doc, reason = "should not panic in practice")] pub fn to_der(&self) -> der::Signature where der::MaxSize: ArraySize, @@ -321,6 +294,7 @@ where /// Normalize signature into "low S" form described in [BIP 0062: Dealing with Malleability][1]. /// /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki + #[must_use] pub fn normalize_s(&self) -> Self { let mut result = *self; let s_inv = ScalarValue::from(-self.s()); @@ -439,11 +413,10 @@ where } } -/// ECDSA [`ObjectIdentifier`] which identifies the digest used by default -/// with the `Signer` and `Verifier` traits. +/// ECDSA [`ObjectIdentifier`] which identifies the digest used by default with the `Signer` and +/// `Verifier` traits. /// -/// To support non-default digest algorithms, use the [`SignatureWithOid`] -/// type instead. +/// To support non-default digest algorithms, use the [`SignatureWithOid`] type instead. #[cfg(feature = "digest")] impl AssociatedOid for Signature where @@ -507,9 +480,8 @@ impl Zeroize for Signature { } } -/// An extended [`Signature`] type which is parameterized by an -/// `ObjectIdentifier` which identifies the ECDSA variant used by a -/// particular signature. +/// An extended [`Signature`] type which is parameterized by an `ObjectIdentifier` which identifies +/// the ECDSA variant used by a particular signature. /// /// Valid `ObjectIdentifiers` are defined in [RFC5758 ยง 3.2]: /// @@ -540,8 +512,7 @@ where { /// Create a new signature with an explicitly provided OID. /// - /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for - /// ECDSA variants. + /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for ECDSA variants. /// /// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 pub fn new(signature: Signature, oid: ObjectIdentifier) -> Result { diff --git a/ecdsa/src/recovery.rs b/ecdsa/src/recovery.rs index 189b691e..f4bccb8d 100644 --- a/ecdsa/src/recovery.rs +++ b/ecdsa/src/recovery.rs @@ -29,17 +29,15 @@ use { /// Recovery IDs, a.k.a. "recid". /// -/// This is an integer value `0`, `1`, `2`, or `3` included along with a -/// signature which is used during the recovery process to select the correct -/// public key from the signature. +/// This is an integer value `0`, `1`, `2`, or `3` included along with a signature which is used +/// during the recovery process to select the correct public key from the signature. /// /// It consists of two bits of information: /// -/// - low bit (0/1): was the y-coordinate of the affine point resulting from -/// the fixed-base multiplication ๐‘˜ร—๐‘ฎ odd? This part of the algorithm -/// functions similar to point decompression. -/// - hi bit (2/3): did the affine x-coordinate of ๐‘˜ร—๐‘ฎ overflow the order of -/// the scalar field, requiring a reduction when computing `r`? +/// 1. low bit (0/1): was the y-coordinate of the affine point resulting from the fixed-base +/// multiplication ๐‘˜ร—๐‘ฎ odd? This part of the algorithm functions similar to point decompression. +/// 2. hi bit (2/3): did the affine x-coordinate of ๐‘˜ร—๐‘ฎ overflow the order of the scalar field `n`, +/// requiring a reduction when computing `r`? #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct RecoveryId(pub(crate) u8); @@ -51,21 +49,26 @@ impl RecoveryId { /// /// - `is_y_odd`: is the affine y-coordinate of ๐‘˜ร—๐‘ฎ odd? /// - `is_x_reduced`: did the affine x-coordinate of ๐‘˜ร—๐‘ฎ overflow the curve order? + #[must_use] + #[allow(clippy::as_conversions, reason = "const fn")] pub const fn new(is_y_odd: bool, is_x_reduced: bool) -> Self { Self(((is_x_reduced as u8) << 1) | (is_y_odd as u8)) } /// Did the affine x-coordinate of ๐‘˜ร—๐‘ฎ overflow the curve order? + #[must_use] pub const fn is_x_reduced(self) -> bool { (self.0 & 0b10) != 0 } /// Is the affine y-coordinate of ๐‘˜ร—๐‘ฎ odd? + #[must_use] pub const fn is_y_odd(self) -> bool { (self.0 & 1) != 0 } /// Convert a `u8` into a [`RecoveryId`]. + #[must_use] pub const fn from_byte(byte: u8) -> Option { if byte <= Self::MAX { Some(Self(byte)) @@ -75,6 +78,7 @@ impl RecoveryId { } /// Convert this [`RecoveryId`] into a `u8`. + #[must_use] pub const fn to_byte(self) -> u8 { self.0 } @@ -82,9 +86,12 @@ impl RecoveryId { #[cfg(feature = "algorithm")] impl RecoveryId { - /// Given a public key, message, and signature, use trial recovery - /// to determine if a suitable recovery ID exists, or return an error - /// otherwise. + /// Given a public key, message, and signature, use trial recovery to determine if a suitable + /// recovery ID exists. + /// + /// # Errors + /// Returns an error if a suitable solution could not be found and/or the signature does not + /// verify. pub fn trial_recovery_from_msg( verifying_key: &VerifyingKey, msg: &[u8], @@ -98,9 +105,12 @@ impl RecoveryId { Self::trial_recovery_from_digest(verifying_key, C::Digest::new_with_prefix(msg), signature) } - /// Given a public key, message digest, and signature, use trial recovery - /// to determine if a suitable recovery ID exists, or return an error - /// otherwise. + /// Given a public key, message digest, and signature, use trial recovery to determine if a + /// suitable recovery ID exists. + /// + /// # Errors + /// Returns an error if a suitable solution could not be found and/or the signature does not + /// verify. pub fn trial_recovery_from_digest( verifying_key: &VerifyingKey, digest: D, @@ -115,9 +125,12 @@ impl RecoveryId { Self::trial_recovery_from_prehash(verifying_key, &digest.finalize(), signature) } - /// Given a public key, message digest, and signature, use trial recovery - /// to determine if a suitable recovery ID exists, or return an error - /// otherwise. + /// Given a public key, message digest, and signature, use trial recovery to determine if a + /// suitable recovery ID exists. + /// + /// # Errors + /// Returns an error if a suitable solution could not be found and/or the signature does not + /// verify. pub fn trial_recovery_from_prehash( verifying_key: &VerifyingKey, prehash: &[u8],