Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do byte comparison in all verify_* functions #269

Merged
merged 4 commits into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@
#![no_std]
#![warn(future_incompatible, rust_2018_idioms)]
#![deny(missing_docs)] // refuse to compile if documentation is missing
#![deny(clippy::unwrap_used)] // don't allow unwrap
#![cfg_attr(not(test), forbid(unsafe_code))]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
Expand Down
3 changes: 3 additions & 0 deletions src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ impl InternalSignature {
/// only checking the most significant three bits. (See also the
/// documentation for [`crate::VerifyingKey::verify_strict`].)
#[inline]
#[allow(clippy::unwrap_used)]
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<InternalSignature, SignatureError> {
// TODO: Use bytes.split_array_ref once it’s in MSRV.
let (lower, upper) = bytes.split_at(32);
Expand All @@ -181,7 +182,9 @@ impl TryFrom<&ed25519::Signature> for InternalSignature {
}

impl From<InternalSignature> for ed25519::Signature {
#[allow(clippy::unwrap_used)]
fn from(sig: InternalSignature) -> ed25519::Signature {
// This function is actually infallible but it's marked as fallible
rozbb marked this conversation as resolved.
Show resolved Hide resolved
ed25519::Signature::from_bytes(&sig.as_bytes()).unwrap()
}
}
23 changes: 7 additions & 16 deletions src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,25 +670,16 @@ impl Drop for ExpandedSecretKey {
}

impl From<&SecretKey> for ExpandedSecretKey {
#[allow(clippy::unwrap_used)]
fn from(secret_key: &SecretKey) -> ExpandedSecretKey {
let mut h: Sha512 = Sha512::default();
let mut hash: [u8; 64] = [0u8; 64];
let mut lower: [u8; 32] = [0u8; 32];
let mut upper: [u8; 32] = [0u8; 32];

h.update(secret_key);
hash.copy_from_slice(h.finalize().as_slice());

lower.copy_from_slice(&hash[00..32]);
upper.copy_from_slice(&hash[32..64]);

lower[0] &= 248;
lower[31] &= 63;
lower[31] |= 64;
let hash = Sha512::default().chain_update(secret_key).finalize();
// TODO: Use bytes.split_array_ref once it’s in MSRV.
let (lower, upper) = hash.split_at(32);
rozbb marked this conversation as resolved.
Show resolved Hide resolved

// The try_into here converts to fixed-size array
ExpandedSecretKey {
key: Scalar::from_bits(lower),
nonce: upper,
key: Scalar::from_bits_clamped(lower.try_into().unwrap()),
nonce: upper.try_into().unwrap(),
}
}
}
Expand Down
40 changes: 18 additions & 22 deletions src/verifying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ use crate::signing::*;
/// considered unequal to the other equivalent encoding, despite the two representing the same
/// point. More encoding details can be found
/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am).
///
/// If you don't care and/or don't want to deal with this, just make sure to use the
/// [`VerifyingKey::verify_strict`] function.
/// If you want to make sure that signatures produced with respect to those sorts of public keys
/// are rejected, use [`VerifyingKey::verify_strict`].
// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
#[derive(Copy, Clone, Default, Eq)]
pub struct VerifyingKey(pub(crate) CompressedEdwardsY, pub(crate) EdwardsPoint);
Expand Down Expand Up @@ -82,8 +81,8 @@ impl PartialEq<VerifyingKey> for VerifyingKey {
impl From<&ExpandedSecretKey> for VerifyingKey {
/// Derive this public key from its corresponding `ExpandedSecretKey`.
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
let mut bits: [u8; 32] = expanded_secret_key.key.to_bytes();
VerifyingKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(&mut bits)
let bits: [u8; 32] = expanded_secret_key.key.to_bytes();
VerifyingKey::clamp_and_mul_base(bits)
}
}

Expand Down Expand Up @@ -151,17 +150,10 @@ impl VerifyingKey {
Ok(VerifyingKey(compressed, point))
}

/// Internal utility function for mangling the bits of a (formerly
/// mathematically well-defined) "scalar" and multiplying it to produce a
/// public key.
fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(
bits: &mut [u8; 32],
) -> VerifyingKey {
bits[0] &= 248;
bits[31] &= 127;
bits[31] |= 64;

let scalar = Scalar::from_bits(*bits);
/// Internal utility function for clamping a scalar representation and multiplying by the
/// basepont to produce a public key.
fn clamp_and_mul_base(bits: [u8; 32]) -> VerifyingKey {
let scalar = Scalar::from_bits_clamped(bits);
let point = EdwardsPoint::mul_base(&scalar);
let compressed = point.compress();

Expand Down Expand Up @@ -195,17 +187,21 @@ impl VerifyingKey {
// Helper function for verification. Computes the _expected_ R component of the signature. The
// caller compares this to the real R component. If `context.is_some()`, this does the
// prehashed variant of the computation using its contents.
// Note that this returns the compressed form of R and the caller does a byte comparison. This
// means that all our verification functions do not accept non-canonically encoded R values.
// See the validation criteria blog post for more details:
// https://hdevalence.ca/blog/2020-10-04-its-25519am
#[allow(non_snake_case)]
fn recompute_r(
&self,
context: Option<&[u8]>,
signature: &InternalSignature,
M: &[u8],
) -> EdwardsPoint {
) -> CompressedEdwardsY {
let k = Self::compute_challenge(context, &signature.R, &self.0, M);
let minus_A: EdwardsPoint = -self.1;
// Recall the (non-batched) verification equation: -[k]A + [s]B = R
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s)
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress()
}

/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
Expand Down Expand Up @@ -246,7 +242,7 @@ impl VerifyingKey {
let message = prehashed_message.finalize();
let expected_R = self.recompute_r(Some(ctx), &signature, &message);

if expected_R.compress() == signature.R {
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
Expand Down Expand Up @@ -334,7 +330,7 @@ impl VerifyingKey {
}

let expected_R = self.recompute_r(None, &signature, message);
if expected_R == signature_R {
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
Expand Down Expand Up @@ -390,7 +386,7 @@ impl VerifyingKey {
let message = prehashed_message.finalize();
let expected_R = self.recompute_r(Some(ctx), &signature, &message);

if expected_R == signature_R {
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
Expand All @@ -409,7 +405,7 @@ impl Verifier<ed25519::Signature> for VerifyingKey {
let signature = InternalSignature::try_from(signature)?;

let expected_R = self.recompute_r(None, &signature, message);
if expected_R.compress() == signature.R {
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
Expand Down