CVE-2019-20399 (published 2020-01-23T00:15Z)

A timing vulnerability in the Scalar::check_overflow function in Parity libsecp256k1-rs before 0.3.1 potentially allows an attacker to leak information via a side-channel attack.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 5.9, MEDIUM (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-203
cwe_name: Observable Discrepancy
description: The product behaves differently or sends different responses under different circumstances in a way that is observable to an unauthorized actor, which exposes security-relevant information about the state of the product, such as whether a particular operation was successful or not.
extended_description: Discrepancies can take many forms, and variations may be detectable in timing, control flow, communications such as replies or requests, or general behavior. These discrepancies can reveal information about the product's operation or internal state to an unauthorized actor. In some cases, discrepancies can be used by attackers to form a side channel.
         

Fix information:
repo_url: https://github.com/paritytech/libsecp256k1
commit_details: repo_url: https://github.com/paritytech/libsecp256k1
author: Wei Tang
author_date: 2019-10-02 18:31:32-07:00
msg: Prevent side channel leak in Scala::check_overflow
repository: repo_url: https://github.com/paritytech/libsecp256k1
repo_name: paritytech/libsecp256k1
description: Pure Rust Implementation of secp256k1.
repo_language: Rust
owner: paritytech
file_changes: filename: Cargo.toml
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@
 name = "libsecp256k1"
 description = "Pure Rust secp256k1 implementation."
 license = "Apache-2.0"
-version = "0.3.0"
+version = "0.4.0"
 authors = ["Wei Tang <hi@that.world>"]
 repository = "https://github.com/sorpaas/libsecp256k1-rs"
 keywords = [ "crypto", "ECDSA", "secp256k1", "bitcoin", "no_std" ]
@@ -18,12 +18,17 @@ sha2 = "0.8"
 digest = "0.8"
 typenum = "1.11"
 arrayref = "0.3"
+subtle = { version = "2.2", default-features = false }
 
 [dev-dependencies]
 secp256k1-test = "0.7"
 clear_on_drop = "0.2"
 rand-test = { package = "rand", version = "0.4" }
 
+[features]
+default = ["std"]
+std = ["subtle/std", "rand/std"]
+
 [workspace]
 members = [
   "./gen/ecmult",

programming_language: Lua
filename: lib.rs
change_type: ModificationType.MODIFY
diff: @@ -7,7 +7,7 @@
         unused_variables, non_shorthand_field_patterns,
         unreachable_code, unused_parens)]
 
-#![no_std]
+#![cfg_attr(not(feature = "std"), no_std)]
 
 #[macro_use]
 mod field;

programming_language: Rust
filename: scalar.rs
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,5 @@
 use core::ops::{Add, AddAssign, Mul, MulAssign};
+use subtle::Choice;
 
 const SECP256K1_N_0: u32 = 0xD0364141;
 const SECP256K1_N_1: u32 = 0xBFD25E8C;
@@ -69,21 +70,21 @@ impl Scalar {
 
     #[must_use]
     fn check_overflow(&self) -> bool {
-        let mut yes: bool = false;
-        let mut no: bool = false;
-        no = no || (self.0[7] < SECP256K1_N_7); /* No need for a > check. */
-        no = no || (self.0[6] < SECP256K1_N_6); /* No need for a > check. */
-        no = no || (self.0[5] < SECP256K1_N_5); /* No need for a > check. */
-        no = no || (self.0[4] < SECP256K1_N_4);
-        yes = yes || ((self.0[4] > SECP256K1_N_4) && !no);
-        no = no || ((self.0[3] < SECP256K1_N_3) && !yes);
-        yes = yes || ((self.0[3] > SECP256K1_N_3) && !no);
-        no = no || ((self.0[2] < SECP256K1_N_2) && !yes);
-        yes = yes || ((self.0[2] > SECP256K1_N_2) && !no);
-        no = no || ((self.0[1] < SECP256K1_N_1) && !yes);
-        yes = yes || ((self.0[1] > SECP256K1_N_1) && !no);
-        yes = yes || ((self.0[0] >= SECP256K1_N_0) && !no);
-        return yes;
+        let mut yes: Choice = 0.into();
+        let mut no: Choice = 0.into();
+        no |= Choice::from((self.0[7] < SECP256K1_N_7) as u8); /* No need for a > check. */
+        no |= Choice::from((self.0[6] < SECP256K1_N_6) as u8); /* No need for a > check. */
+        no |= Choice::from((self.0[5] < SECP256K1_N_5) as u8); /* No need for a > check. */
+        no |= Choice::from((self.0[4] < SECP256K1_N_4) as u8);
+        yes |= Choice::from((self.0[4] > SECP256K1_N_4) as u8) & !no;
+        no |= Choice::from((self.0[3] < SECP256K1_N_3) as u8) & !yes;
+        yes |= Choice::from((self.0[3] > SECP256K1_N_3) as u8) & !no;
+        no |= Choice::from((self.0[2] < SECP256K1_N_2) as u8) & !yes;
+        yes |= Choice::from((self.0[2] > SECP256K1_N_2) as u8) & !no;
+        no |= Choice::from((self.0[1] < SECP256K1_N_1) as u8) & !yes;
+        yes |= Choice::from((self.0[1] > SECP256K1_N_1) as u8) & !no;
+        yes |= Choice::from((self.0[0] >= SECP256K1_N_0) as u8) & !no;
+        return yes.into();
     }
 
     #[must_use]

programming_language: Rust