diff options
Diffstat (limited to 'vendor/ecdsa/src/recovery.rs')
-rw-r--r-- | vendor/ecdsa/src/recovery.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/vendor/ecdsa/src/recovery.rs b/vendor/ecdsa/src/recovery.rs new file mode 100644 index 000000000..c923bbad7 --- /dev/null +++ b/vendor/ecdsa/src/recovery.rs @@ -0,0 +1,110 @@ +//! Public key recovery support. + +use crate::{Error, Result}; + +/// 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. +/// +/// 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 (3/4): did the affine x-coordinate of 𝑘×𝑮 overflow the order of +/// the scalar field, requiring a reduction when computing `r`? +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct RecoveryId(u8); + +impl RecoveryId { + /// Maximum supported value for the recovery ID (inclusive). + pub const MAX: u8 = 3; + + /// Create a new [`RecoveryId`] from the following 1-bit arguments: + /// + /// - `is_y_odd`: is the affine y-coordinate of 𝑘×𝑮 odd? + /// - `is_x_reduced`: did the affine x-coordinate of 𝑘×𝑮 overflow the curve order? + 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? + pub const fn is_x_reduced(self) -> bool { + (self.0 & 0b10) != 0 + } + + /// Is the affine y-coordinate of 𝑘×𝑮 odd? + pub const fn is_y_odd(self) -> bool { + (self.0 & 1) != 0 + } + + /// Convert a `u8` into a [`RecoveryId`]. + pub const fn from_byte(byte: u8) -> Option<Self> { + if byte <= Self::MAX { + Some(Self(byte)) + } else { + None + } + } + + /// Convert this [`RecoveryId`] into a `u8`. + pub const fn to_byte(self) -> u8 { + self.0 + } +} + +impl TryFrom<u8> for RecoveryId { + type Error = Error; + + fn try_from(byte: u8) -> Result<Self> { + Self::from_byte(byte).ok_or_else(Error::new) + } +} + +impl From<RecoveryId> for u8 { + fn from(id: RecoveryId) -> u8 { + id.0 + } +} + +#[cfg(test)] +mod tests { + use super::RecoveryId; + + #[test] + fn new() { + assert_eq!(RecoveryId::new(false, false).to_byte(), 0); + assert_eq!(RecoveryId::new(true, false).to_byte(), 1); + assert_eq!(RecoveryId::new(false, true).to_byte(), 2); + assert_eq!(RecoveryId::new(true, true).to_byte(), 3); + } + + #[test] + fn try_from() { + for n in 0u8..=3 { + assert_eq!(RecoveryId::try_from(n).unwrap().to_byte(), n); + } + + for n in 4u8..=255 { + assert!(RecoveryId::try_from(n).is_err()); + } + } + + #[test] + fn is_x_reduced() { + assert_eq!(RecoveryId::try_from(0).unwrap().is_x_reduced(), false); + assert_eq!(RecoveryId::try_from(1).unwrap().is_x_reduced(), false); + assert_eq!(RecoveryId::try_from(2).unwrap().is_x_reduced(), true); + assert_eq!(RecoveryId::try_from(3).unwrap().is_x_reduced(), true); + } + + #[test] + fn is_y_odd() { + assert_eq!(RecoveryId::try_from(0).unwrap().is_y_odd(), false); + assert_eq!(RecoveryId::try_from(1).unwrap().is_y_odd(), true); + assert_eq!(RecoveryId::try_from(2).unwrap().is_y_odd(), false); + assert_eq!(RecoveryId::try_from(3).unwrap().is_y_odd(), true); + } +} |