Repository: google/OpenSK
File: libraries/crypto/src/ecdsa.rs (Rust)

Code before:
    #[cfg(feature = "std")]
    pub fn from_bytes_uncompressed(bytes: &[u8]) -> Option<PubKey> {
        PointP256::from_bytes_uncompressed_vartime(bytes).map(|p| PubKey { p })
    }

    #[cfg(test)]
    fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
        self.p.to_bytes_uncompressed(bytes);
    }

    #[cfg(feature = "with_ctap1")]
    pub fn to_uncompressed(&self) -> [u8; PubKey::UNCOMPRESSED_LENGTH] {
        // Formatting according to:
        // https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html#overview
        const B0_BYTE_MARKER: u8 = 0x04;
        let mut representation = [0; PubKey::UNCOMPRESSED_LENGTH];
        let (marker, x, y) =
            mut_array_refs![&mut representation, 1, int256::NBYTES, int256::NBYTES];
        marker[0] = B0_BYTE_MARKER;
        self.p.getx().to_int().to_bin(x);
        self.p.gety().to_int().to_bin(y);
        representation
    }

    /// Writes the coordinates into the passed in arrays.
    pub fn to_coordinates(&self, x: &mut [u8; NBYTES], y: &mut [u8; NBYTES]) {
        self.p.getx().to_int().to_bin(x);
        self.p.gety().to_int().to_bin(y);
    }

    pub fn verify_hash_vartime(&self, hash: &[u8; NBYTES], sign: &Signature) -> bool {
        let m = ExponentP256::modn(Int256::from_bin(hash));

        let v = sign.s.inv();
        let u = &m * v.as_exponent();
        let v = &sign.r * &v;

        let u = self.p.points_mul(&u, v.as_exponent()).getx();

        ExponentP256::modn(u.to_int()) == *sign.r.as_exponent()
    }

    #[cfg(feature = "std")]
    pub fn verify_vartime<H>(&self, msg: &[u8], sign: &Signature) -> bool
    where
        H: Hash256,
    {
        self.verify_hash_vartime(&H::hash(msg), sign)
    }
}


Review comment (suggestion):
overall comment regarding all methods which previously were std only: these functions were not previously meant to be run in the firmware and were not part of the "best-effort contant time".
if they are now used in a critical path, they may require to be rewritten.

Code after:
        PointP256::new_checked_vartime(Int256::from_bin(x), Int256::from_bin(y))
            .map(|p| PubKey { p })
    }

    #[cfg(feature = "std")]
    pub fn from_bytes_uncompressed(bytes: &[u8]) -> Option<PubKey> {
        PointP256::from_bytes_uncompressed_vartime(bytes).map(|p| PubKey { p })
    }

    #[cfg(test)]
    fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
        self.p.to_bytes_uncompressed(bytes);
    }

    #[cfg(feature = "with_ctap1")]
    pub fn to_uncompressed(&self) -> [u8; PubKey::UNCOMPRESSED_LENGTH] {
        // Formatting according to:
        // https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html#overview
        const B0_BYTE_MARKER: u8 = 0x04;
        let mut representation = [0; PubKey::UNCOMPRESSED_LENGTH];
        let (marker, x, y) =
            mut_array_refs![&mut representation, 1, int256::NBYTES, int256::NBYTES];
        marker[0] = B0_BYTE_MARKER;
        self.p.getx().to_int().to_bin(x);
        self.p.gety().to_int().to_bin(y);
        representation
    }

    /// Writes the coordinates into the passed in arrays.
    pub fn to_coordinates(&self, x: &mut [u8; NBYTES], y: &mut [u8; NBYTES]) {
        self.p.getx().to_int().to_bin(x);
        self.p.gety().to_int().to_bin(y);
    }

    /// Verifies if the data's hash matches its signature.
    ///
    /// This function is not a constant time implementation, and does not resist side channel
    /// attacks. Only use if all data involved is public knowledge.
    pub fn verify_hash_vartime(&self, hash: &[u8; NBYTES], sign: &Signature) -> bool {
        let m = ExponentP256::modn(Int256::from_bin(hash));

        let v = sign.s.inv();
        let u = &m * v.as_exponent();
        let v = &sign.r * &v;

        let u = self.p.points_mul(&u, v.as_exponent()).getx();

        ExponentP256::modn(u.to_int()) == *sign.r.as_exponent()
    }

    #[cfg(feature = "std")]