HKDF and compare constant time in Rust I have implemented HKDF in Rust, which passes all test vectors from RFC 5869, using HKDF with HMAC-SHA256. I also have a function to compare byte arrays in constant time, which I hope someone could confirm is correctly done. Any type of feedback is welcome.

EDIT: I mainly mean feedback on hkdf_compute(). HMAC in hkdf_extract() is defined in another module and not included here.

HKDF

pub struct Hkdf {
  pub salt: Vec&lt;u8&gt;,
  pub data: Vec&lt;u8&gt;,
  pub info: Vec&lt;u8&gt;,
  pub hmac: usize,
  pub length: usize,
}

impl Drop for Hkdf {
  fn drop(&amp;mut self) {
    //println!("DROPPING");
    self.salt.clear();
    self.data.clear();
    self.info.clear()
  }
}

impl Hkdf {
  /// Return HMAC matching argument passsed to Hkdf.
  pub fn hkdf_extract(&amp;self, data: &amp;[u8], salt: &amp;[u8]) -&gt; Vec&lt;u8&gt; {
    let hmac_res = Hmac {
        secret_key: salt.to_vec(),
        message: data.to_vec(),
        sha2: self.hmac
    };

    hmac_res.hmac_compute()
  }

  /// The HKDF Expand step. Returns an HKDF.
  pub fn hkdf_compute(&amp;self) -&gt; Vec&lt;u8&gt; {
    // Check that the selected key length is within the limit.
    if self.length as f32 &gt; 255_f32 * (self.hmac / 8) as f32 {
        panic!("Derived key length above max. 255 * (HMAC OUTPUT LENGTH IN BYTES)");
    }

    let n_iter = (self.length as f32 / (self.hmac / 8) as f32).ceil() as usize;

    let mut con_step: Vec&lt;u8&gt; = vec![];
    let mut t_step: Vec&lt;u8&gt; = vec![];
    let mut hkdf_final: Vec&lt;u8&gt; = vec![];

    for x in 1..n_iter+1 {
            con_step.append(&amp;mut t_step);
            con_step.extend_from_slice(&amp;self.info);
            con_step.push(x as u8);
            t_step.extend_from_slice(&amp;self.hkdf_extract(
                &amp;con_step,
                &amp;self.hkdf_extract(&amp;self.data, &amp;self.salt))
            );
            con_step.clear();

            hkdf_final.extend_from_slice(&amp;t_step);
    }

    hkdf_final.truncate(self.length);

    hkdf_final
  }
}


Compare constant time:

#[inline(never)]
/// Comparison in constant time.
pub fn compare_ct(x: &amp;[u8], y: &amp;[u8]) -&gt; bool {

  let length = x.len();

  if length != y.len() {
    false;
  }

  let mut result: u8 = 0;

  for n in 0..length {
    result |= x[n] ^ y[n];
  }

  result == 0
}
In general you should try and avoid floating point operations for any cryptographically specific code. Floating points have issues with regards to precision, and even I don't see any issue for this specific code, they always require special attention, e.g. during reviewing of the code.

Similarly, using recursive code is strongly discouraged for cryptographic algorithms. If your code can be coded using loops and only local variables you should really do so. I imagine that goes against the idea of Rust (and is thus up for debate), but crypto code generally requires special attention. If you keep to recursive calls make sure you document them as such, both inline and in the function description.

Although you nicely implemented the Drop destructor it makes more sense to create either a class with just a keyInputMaterial (now called data I presume) or simply keep all the data in parameters / local variables. The idea of just keeping the keyInputMaterial is that you may reuse the object to derive other keys using the function but with different salt / info. In that case you obviously need to retrain the Drop to clear the input key material.

Note that the internal key handling (padding, initial hashing) of HMAC is always the same. So a sneaky speed up is to perform pre-calculation on the salt which acts as the HMAC key (mainly useful if there is a lot of output expected, this speedup matters more for e.g. PBKDF2).



Your constant time function looks very much run-of-the-mill constant time, which in this case is great :) I don't think HKDF warrants a function where HMAC(k, x) and HMAC(k, y) are compared using a random key - but now you know about that neat little trick.

Note that you may want to make sure that you document that the comparison needs to be done in constant time or include it in a verify function (beware of truncated values - you did this in your code, but you need to consider it in the calling code as well!).



Note that I'm not a Rust expert, so I won't comment on the vector handling / slicing etc.