Skip to content

Commit c0eac0f

Browse files
authored
Clean up repeated constant time bytes comparison (#12996)
1 parent 56005fb commit c0eac0f

File tree

7 files changed

+28
-14
lines changed

7 files changed

+28
-14
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This file is dual licensed under the terms of the Apache License, Version
2+
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
3+
// for complete details.
4+
5+
/// Performs a constant-time comparison of two byte slices.
6+
pub fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
7+
if a.len() != b.len() {
8+
return false;
9+
}
10+
11+
openssl::memcmp::eq(a, b)
12+
}

src/rust/cryptography-crypto/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
// for complete details.
44

5+
pub mod constant_time;
56
pub mod encoding;
67
pub mod pbkdf1;
78
pub mod pkcs12;

src/rust/src/backend/cmac.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
// for complete details.
44

5+
use cryptography_crypto::constant_time;
56
use pyo3::types::{PyAnyMethods, PyBytesMethods};
67

78
use crate::backend::cipher_registry;
@@ -84,7 +85,7 @@ impl Cmac {
8485
fn verify(&mut self, py: pyo3::Python<'_>, signature: &[u8]) -> CryptographyResult<()> {
8586
let actual = self.finalize(py)?;
8687
let actual = actual.as_bytes();
87-
if actual.len() != signature.len() || !openssl::memcmp::eq(actual, signature) {
88+
if !constant_time::bytes_eq(actual, signature) {
8889
return Err(CryptographyError::from(
8990
exceptions::InvalidSignature::new_err("Signature did not match digest."),
9091
));

src/rust/src/backend/hmac.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
// for complete details.
44

5+
use cryptography_crypto::constant_time;
56
use pyo3::types::PyBytesMethods;
67

78
use crate::backend::hashes::message_digest_from_algorithm;
@@ -90,7 +91,7 @@ impl Hmac {
9091
fn verify(&mut self, py: pyo3::Python<'_>, signature: &[u8]) -> CryptographyResult<()> {
9192
let actual_bound = self.finalize(py)?;
9293
let actual = actual_bound.as_bytes();
93-
if actual.len() != signature.len() || !openssl::memcmp::eq(actual, signature) {
94+
if !constant_time::bytes_eq(actual, signature) {
9495
return Err(CryptographyError::from(
9596
exceptions::InvalidSignature::new_err("Signature did not match digest."),
9697
));

src/rust/src/backend/kdf.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use base64::engine::general_purpose::STANDARD_NO_PAD;
77
#[cfg(CRYPTOGRAPHY_OPENSSL_320_OR_GREATER)]
88
use base64::engine::Engine;
99
#[cfg(not(CRYPTOGRAPHY_IS_LIBRESSL))]
10+
use cryptography_crypto::constant_time;
11+
#[cfg(not(CRYPTOGRAPHY_IS_LIBRESSL))]
1012
use pyo3::types::PyBytesMethods;
1113

1214
use crate::backend::hashes;
@@ -152,9 +154,7 @@ impl Scrypt {
152154
let actual_bytes = actual.as_bytes();
153155
let expected_bytes = expected_key.as_bytes();
154156

155-
if actual_bytes.len() != expected_bytes.len()
156-
|| !openssl::memcmp::eq(actual_bytes, expected_bytes)
157-
{
157+
if !constant_time::bytes_eq(actual_bytes, expected_bytes) {
158158
return Err(CryptographyError::from(exceptions::InvalidKey::new_err(
159159
"Keys do not match.",
160160
)));
@@ -314,9 +314,7 @@ impl Argon2id {
314314
let actual_bytes = actual.as_bytes();
315315
let expected_bytes = expected_key.as_bytes();
316316

317-
if actual_bytes.len() != expected_bytes.len()
318-
|| !openssl::memcmp::eq(actual_bytes, expected_bytes)
319-
{
317+
if !constant_time::bytes_eq(actual_bytes, expected_bytes) {
320318
return Err(CryptographyError::from(exceptions::InvalidKey::new_err(
321319
"Keys do not match.",
322320
)));
@@ -435,9 +433,7 @@ impl Argon2id {
435433
let derived_key = argon2.derive(py, key_material)?;
436434
let derived_bytes = derived_key.as_bytes();
437435

438-
if derived_bytes.len() != hash_bytes.len()
439-
|| !openssl::memcmp::eq(derived_bytes, &hash_bytes)
440-
{
436+
if !constant_time::bytes_eq(derived_bytes, &hash_bytes) {
441437
return Err(CryptographyError::from(exceptions::InvalidKey::new_err(
442438
"Keys do not match.",
443439
)));

src/rust/src/backend/poly1305.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
// for complete details.
44

5+
use cryptography_crypto::constant_time;
56
use pyo3::types::PyBytesMethods;
67

78
use crate::buf::CffiBuf;
@@ -191,7 +192,7 @@ impl Poly1305 {
191192
fn verify(&mut self, py: pyo3::Python<'_>, signature: &[u8]) -> CryptographyResult<()> {
192193
let actual_bound = self.finalize(py)?;
193194
let actual = actual_bound.as_bytes();
194-
if actual.len() != signature.len() || !openssl::memcmp::eq(actual, signature) {
195+
if !constant_time::bytes_eq(actual, signature) {
195196
return Err(CryptographyError::from(
196197
exceptions::InvalidSignature::new_err("Value did not match computed tag."),
197198
));

src/rust/src/buf.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
// for complete details.
44

5+
use std::slice;
6+
7+
use pyo3::types::PyAnyMethods;
8+
59
#[cfg(not(Py_3_11))]
610
use crate::types;
7-
use pyo3::types::PyAnyMethods;
8-
use std::slice;
911

1012
// Common error message generation
1113
fn generate_non_convertible_buffer_error_msg(pyobj: &pyo3::Bound<'_, pyo3::PyAny>) -> String {

0 commit comments

Comments
 (0)