[pbs-devel] [PATCH proxmox 06/12] sys: crypt: use constant time comparison for password verification

Max Carrara m.carrara at proxmox.com
Mon Feb 19 17:11:09 CET 2024


On 2/15/24 16:19, Stefan Sterz wrote:
> by using `openssl::memcmp::eq()` we can avoid potential timing side
> channels as its runtime only depends on the length of the arrays, not
> the contents. this requires the two arrays to have the same length, but
> that should be a given since the hashes should always have the same
> length.
> 
> Signed-off-by: Stefan Sterz <s.sterz at proxmox.com>

See my reply to patch 04 - the usage of `openssl::memcmp::eq()` in the legacy
code block there could be merged with this commit first before moving to / implementing
HMAC.

LGTM otherwise, but see the comment inline.

> ---
>  proxmox-sys/Cargo.toml   | 3 ++-
>  proxmox-sys/src/crypt.rs | 8 +++++++-
>  2 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/proxmox-sys/Cargo.toml b/proxmox-sys/Cargo.toml
> index 5ddbe21..1a44702 100644
> --- a/proxmox-sys/Cargo.toml
> +++ b/proxmox-sys/Cargo.toml
> @@ -16,6 +16,7 @@ lazy_static.workspace = true
>  libc.workspace = true
>  log.workspace = true
>  nix.workspace = true
> +openssl = { workspace = true, optional = true }
>  regex.workspace = true
>  serde_json.workspace = true
>  serde = { workspace = true, features = [ "derive" ] }
> @@ -29,5 +30,5 @@ proxmox-time.workspace = true
>  default = []
>  logrotate = ["dep:zstd"]
>  acl = []
> -crypt = []
> +crypt = ["dep:openssl"]
>  timer = []
> diff --git a/proxmox-sys/src/crypt.rs b/proxmox-sys/src/crypt.rs
> index fa10911..3313f66 100644
> --- a/proxmox-sys/src/crypt.rs
> +++ b/proxmox-sys/src/crypt.rs
> @@ -155,9 +155,15 @@ pub fn encrypt_pw(password: &str) -> Result<String, Error> {
>  /// Verify if an encrypted password matches
>  pub fn verify_crypt_pw(password: &str, enc_password: &str) -> Result<(), Error> {
>      let verify = crypt(password.as_bytes(), enc_password.as_bytes())?;
> -    if verify != enc_password {
> +
> +    // `openssl::memcmp::eq()`'s runtime does not depend on the content of the arrays only the
> +    // length, this makes it harder to exploit timing side-channels.
> +    if verify.len() != enc_password.len()
> +        || !openssl::memcmp::eq(verify.as_bytes(), enc_password.as_bytes())

Like in my comment on patch 04, would it make sense here to split these checks into two
for more fine-grained error messaging? Or are there any reasons why they should be together?

> +    {
>          bail!("invalid credentials");
>      }
> +
>      Ok(())
>  }
>  





More information about the pbs-devel mailing list