Repository: google/OpenSK
File: src/ctap/mod.rs (Rust)

Code before:
    hasher.update(partition_slice);
    let computed_hash = hasher.finalize();
    if &computed_hash != array_ref!(metadata, 0, 32) {
        return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
    }

    verify_signature(
        array_ref!(metadata, 32, 64),
        public_key_bytes,
        &computed_hash,
    )?;
    Ok(())
}

/// Verifies the signature over the given hash.
///
/// The public key is COSE encoded, and the hash is a SHA256.
fn verify_signature(
    signature_bytes: &[u8; 64],
    public_key_bytes: &[u8; 64],
    signed_hash: &[u8; 32],
) -> Result<(), Ctap2StatusCode> {
    let signature = ecdsa::Signature::from_bytes(signature_bytes)
        .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
    let x = array_ref!(public_key_bytes, 0, 32);
    let y = array_ref!(public_key_bytes, 32, 32);
    let public_key = ecdsa::PubKey::from_coordinates(x, y)
        .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
    if !public_key.verify_hash_vartime(signed_hash, &signature) {
        return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
    }
    Ok(())
}

// Sends keepalive packet during user presence checking. If user agent replies with CANCEL response,
// returns Err(UserPresenceError::Canceled).
fn send_keepalive_up_needed(
    env: &mut impl Env,
    channel: Channel,
    timeout: Milliseconds<ClockInt>,
) -> Result<(), UserPresenceError> {
    let (cid, transport) = match channel {
        Channel::MainHid(cid) => (cid, Transport::MainHid),
        #[cfg(feature = "vendor_hid")]
        Channel::VendorHid(cid) => (cid, Transport::VendorHid),
    };
    let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
    for mut pkt in keepalive_msg {
        let ctap_hid_connection = transport.hid_connection(env);
        match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout) {
            Ok(SendOrRecvStatus::Timeout) => {

Review comment (suggestion):
Looks like we could use `PubKey::from_bytes_uncompressed` and directly the 65 bytes in `build.rs`. No strong preference, but `from_bytes_uncompressed` would make both call sites smaller for just 1 more byte stored in the binary.

Code after:
    hasher.update(partition_slice);
    let computed_hash = hasher.finalize();
    if &computed_hash != array_ref!(metadata, 0, 32) {
        return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
    }

    verify_signature(
        array_ref!(metadata, 32, 64),
        public_key_bytes,
        &computed_hash,
    )?;
    Ok(())
}

/// Verifies the signature over the given hash.
///
/// The public key is COSE encoded, and the hash is a SHA256.
fn verify_signature(
    signature_bytes: &[u8; 64],
    public_key_bytes: &[u8],
    signed_hash: &[u8; 32],
) -> Result<(), Ctap2StatusCode> {
    let signature = ecdsa::Signature::from_bytes(signature_bytes)
        .ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
    let public_key = ecdsa::PubKey::from_bytes_uncompressed(public_key_bytes)
        .ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
    if !public_key.verify_hash_vartime(signed_hash, &signature) {
        return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
    }
    Ok(())
}

// Sends keepalive packet during user presence checking. If user agent replies with CANCEL response,
// returns Err(UserPresenceError::Canceled).
fn send_keepalive_up_needed(
    env: &mut impl Env,
    channel: Channel,
    timeout: Milliseconds<ClockInt>,
) -> Result<(), UserPresenceError> {
    let (cid, transport) = match channel {
        Channel::MainHid(cid) => (cid, Transport::MainHid),
        #[cfg(feature = "vendor_hid")]
        Channel::VendorHid(cid) => (cid, Transport::VendorHid),
    };
    let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
    for mut pkt in keepalive_msg {
        let ctap_hid_connection = transport.hid_connection(env);
        match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout) {
            Ok(SendOrRecvStatus::Timeout) => {
                debug_ctap!(env, "Sending a KEEPALIVE packet timed out");
                // TODO: abort user presence test?