Add on-by-default fast crate feature for gating basepoint tables (#251)

* Add on-by-default `fast` crate feature

Disabling the feature reduces overall code size at the cost of
performance, which is useful for e.g. embedded users.

This feature transitively enables the `basepoint-tables` feature in
`curve25519-dalek` where the basepoint tables are actually defined.

* Consolidated a lot of verification code

* Bump `curve25519-dalek`; use `precomputed-tables` feature

The feature name changed in dalek-cryptography/curve25519-dalek#499

Co-authored-by: Michael Rosenberg <michael@mrosenberg.pub>
This commit is contained in:
Tony Arcieri 2023-01-20 13:46:17 -07:00 committed by GitHub
parent 431e69959d
commit f61e9dcf9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 36 deletions

View file

@ -26,13 +26,19 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }} - run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }} - run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --no-default-features --lib - run: cargo test --target ${{ matrix.target }} --no-default-features --lib --tests
- run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib - run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc --lib
- run: cargo test --target ${{ matrix.target }} --no-default-features --features fast --lib
- run: cargo test --target ${{ matrix.target }} --no-default-features --features rand_core --lib --tests
- run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,rand_core --lib --tests
- run: cargo test --target ${{ matrix.target }} --no-default-features --features fast,rand_core --lib --tests
- run: cargo test --target ${{ matrix.target }} --no-default-features --features alloc,fast,rand_core --lib --tests
- run: cargo test --target ${{ matrix.target }} - run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --features batch - run: cargo test --target ${{ matrix.target }} --features batch
- run: cargo test --target ${{ matrix.target }} --features "digest rand_core" - run: cargo test --target ${{ matrix.target }} --features digest,rand_core
- run: cargo test --target ${{ matrix.target }} --features serde - run: cargo test --target ${{ matrix.target }} --features serde
- run: cargo test --target ${{ matrix.target }} --features pem - run: cargo test --target ${{ matrix.target }} --features pem
- run: cargo test --target ${{ matrix.target }} --all-features
build-simd: build-simd:
name: Test simd backend (nightly) name: Test simd backend (nightly)

2
Cargo.lock generated
View file

@ -240,7 +240,7 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "4.0.0-pre.5" version = "4.0.0-pre.5"
source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#83f6b149d33c37b8997316cb7a87d8d247b75c3e" source = "git+https://github.com/dalek-cryptography/curve25519-dalek.git#3effd73307a606e44469a425974f0b7b0eb85899"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"digest", "digest",

View file

@ -51,12 +51,13 @@ harness = false
required-features = ["rand_core"] required-features = ["rand_core"]
[features] [features]
default = ["std", "zeroize"] default = ["fast", "std", "zeroize"]
alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"] alloc = ["curve25519-dalek/alloc", "ed25519/alloc", "serde?/alloc", "zeroize/alloc"]
std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"] std = ["alloc", "ed25519/std", "serde?/std", "sha2/std"]
asm = ["sha2/asm"] asm = ["sha2/asm"]
batch = ["alloc", "merlin", "rand_core"] batch = ["alloc", "merlin", "rand_core"]
fast = ["curve25519-dalek/precomputed-tables"]
digest = [] digest = []
# This features turns off stricter checking for scalar malleability in signatures # This features turns off stricter checking for scalar malleability in signatures
legacy_compatibility = [] legacy_compatibility = []

View file

@ -169,7 +169,8 @@ impl VerifyingKey {
VerifyingKey(compressed, point) VerifyingKey(compressed, point)
} }
// A helper function that computes H(R || A || M) as well as its prehashed version // A helper function that computes H(R || A || M). If `context.is_some()`, this does the
// prehashed variant of the computation using its contents.
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn compute_challenge( fn compute_challenge(
context: Option<&[u8]>, context: Option<&[u8]>,
@ -191,6 +192,22 @@ impl VerifyingKey {
Scalar::from_hash(h) Scalar::from_hash(h)
} }
// 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.
#[allow(non_snake_case)]
fn recompute_r(
&self,
context: Option<&[u8]>,
signature: &InternalSignature,
M: &[u8],
) -> EdwardsPoint {
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)
}
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm. /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
/// ///
/// # Inputs /// # Inputs
@ -226,17 +243,10 @@ impl VerifyingKey {
"The context must not be longer than 255 octets." "The context must not be longer than 255 octets."
); );
let minus_A: EdwardsPoint = -self.1; let message = prehashed_message.finalize();
let k = Self::compute_challenge( let expected_R = self.recompute_r(Some(ctx), &signature, &message);
Some(ctx),
&signature.R,
&self.0,
prehashed_message.finalize().as_slice(),
);
let R: EdwardsPoint =
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s);
if R.compress() == signature.R { if expected_R.compress() == signature.R {
Ok(()) Ok(())
} else { } else {
Err(InternalError::Verify.into()) Err(InternalError::Verify.into())
@ -323,12 +333,8 @@ impl VerifyingKey {
return Err(InternalError::Verify.into()); return Err(InternalError::Verify.into());
} }
let minus_A: EdwardsPoint = -self.1; let expected_R = self.recompute_r(None, &signature, message);
let k = Self::compute_challenge(None, &signature.R, &self.0, message); if expected_R == signature_R {
let R: EdwardsPoint =
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s);
if R == signature_R {
Ok(()) Ok(())
} else { } else {
Err(InternalError::Verify.into()) Err(InternalError::Verify.into())
@ -381,16 +387,10 @@ impl VerifyingKey {
return Err(InternalError::Verify.into()); return Err(InternalError::Verify.into());
} }
let minus_A: EdwardsPoint = -self.1; let message = prehashed_message.finalize();
let k = Self::compute_challenge( let expected_R = self.recompute_r(Some(ctx), &signature, &message);
Some(ctx),
&signature.R,
&self.0,
prehashed_message.finalize().as_slice(),
);
let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s);
if R == signature_R { if expected_R == signature_R {
Ok(()) Ok(())
} else { } else {
Err(InternalError::Verify.into()) Err(InternalError::Verify.into())
@ -408,12 +408,8 @@ impl Verifier<ed25519::Signature> for VerifyingKey {
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> { fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
let signature = InternalSignature::try_from(signature)?; let signature = InternalSignature::try_from(signature)?;
let minus_A: EdwardsPoint = -self.1; let expected_R = self.recompute_r(None, &signature, message);
let k = Self::compute_challenge(None, &signature.R, &self.0, message); if expected_R.compress() == signature.R {
let R: EdwardsPoint =
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s);
if R.compress() == signature.R {
Ok(()) Ok(())
} else { } else {
Err(InternalError::Verify.into()) Err(InternalError::Verify.into())