Skip to content

fix(core): add attestation sig validation #794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions crates/core/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ impl Body {
///
/// See [module level documentation](crate::attestation) for more information.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "validation::AttestationUnchecked")]
pub struct Attestation {
/// The signature of the attestation.
pub signature: Signature,
Expand All @@ -287,3 +288,73 @@ impl Attestation {
PresentationBuilder::new(provider, self)
}
}

/// Invalid attestation error.
#[derive(Debug, thiserror::Error)]
#[error("invalid attestation: {0}")]
pub struct InvalidAttestation(String);

mod validation {
use super::*;
use crate::{serialize::CanonicalSerialize, signing::SignatureVerifierProvider};

#[derive(Debug, Deserialize)]
pub(super) struct AttestationUnchecked {
signature: Signature,
header: Header,
body: Body,
}

impl TryFrom<AttestationUnchecked> for Attestation {
type Error = InvalidAttestation;

fn try_from(unchecked: AttestationUnchecked) -> Result<Self, Self::Error> {
let provider = SignatureVerifierProvider::default();
let verifier = provider.get(&unchecked.signature.alg).map_err(|_| {
InvalidAttestation(format!(
"invalid signature algorithm id {:?}",
unchecked.signature.alg
))
})?;

verifier
.verify(
&unchecked.body.verifying_key.data,
&CanonicalSerialize::serialize(&unchecked.header),
&unchecked.signature.data,
)
.map_err(|_| InvalidAttestation("failed to verify the signature".into()))?;

Ok(Self {
body: unchecked.body,
header: unchecked.header,
signature: unchecked.signature,
})
}
}

#[cfg(test)]
mod tests {
use crate::{attestation::Attestation, fixtures::basic_attestation_fixture};

#[test]
fn test_validation_ok() {
let attestation = basic_attestation_fixture();
let bytes = bincode::serialize(&attestation).unwrap();
let result: Result<Attestation, Box<bincode::ErrorKind>> = bincode::deserialize(&bytes);
assert!(result.is_ok());
}

#[test]
fn test_validation_err() {
let mut attestation = basic_attestation_fixture();

// Corrupt the signature.
attestation.signature.data[1] = attestation.signature.data[1].wrapping_add(1);

let bytes = bincode::serialize(&attestation).unwrap();
let result: Result<Attestation, Box<bincode::ErrorKind>> = bincode::deserialize(&bytes);
assert!(result.is_err());
}
}
}
28 changes: 27 additions & 1 deletion crates/core/src/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ pub use provider::FixtureEncodingProvider;

use hex::FromHex;
use p256::ecdsa::SigningKey;
use tlsn_data_fixtures::http::{request::GET_WITH_HEADER, response::OK_JSON};

use crate::{
attestation::{Attestation, AttestationConfig, Extension},
connection::{
Certificate, ConnectionInfo, HandshakeData, HandshakeDataV1_2, KeyType, ServerCertData,
ServerEphemKey, ServerName, ServerSignature, SignatureScheme, TlsVersion, TranscriptLength,
},
hash::HashAlgorithm,
hash::{Blake3, HashAlgorithm},
request::{Request, RequestConfig},
signing::SignatureAlgId,
transcript::{
Expand Down Expand Up @@ -263,3 +264,28 @@ pub fn attestation_fixture(

attestation_builder.build(&provider).unwrap()
}

/// Returns a basic attestation fixture for testing.
pub fn basic_attestation_fixture() -> Attestation {
let transcript = Transcript::new(GET_WITH_HEADER, OK_JSON);
let connection = ConnectionFixture::tlsnotary(transcript.length());

let RequestFixture {
mut request,
encoding_tree: _,
} = request_fixture(
transcript.clone(),
encoding_provider(GET_WITH_HEADER, OK_JSON),
connection.clone(),
Blake3::default(),
Vec::new(),
);

request.encoding_commitment_root = None;
attestation_fixture(
request,
connection,
SignatureAlgId::SECP256K1,
encoder_secret(),
)
}
Loading