Skip to content
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
85 changes: 84 additions & 1 deletion crates/core/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use crate::{
index::Index,
merkle::MerkleTree,
presentation::PresentationBuilder,
serialize::CanonicalSerialize,
signing::{Signature, VerifyingKey},
transcript::{encoding::EncodingCommitment, hash::PlaintextHash},
CryptoProvider,
Expand Down Expand Up @@ -263,7 +264,7 @@ impl Body {
/// An attestation document.
///
/// See [module level documentation](crate::attestation) for more information.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Attestation {
/// The signature of the attestation.
pub signature: Signature,
Expand All @@ -286,4 +287,86 @@ impl Attestation {
) -> PresentationBuilder<'a> {
PresentationBuilder::new(provider, self)
}

/// Validates the `unchecked` attestation, returning a validated one.
pub fn try_from_unchecked(
unchecked: AttestationUnchecked,
provider: &CryptoProvider,
) -> Result<Attestation, InvalidAttestation> {
let verifier = provider
.signature
.get(&unchecked.signature.alg)
.map_err(|_| {
InvalidAttestation(format!(
"invalid signature algorithm id {:?}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite accurate. The algorithm id isn't necessarily invalid, but rather the provider is not configured with a signature verifier for this 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,
})
}
}

#[doc(hidden)]
#[derive(Debug, Deserialize)]
#[serde(from = "Attestation")]
pub struct AttestationUnchecked {
signature: Signature,
header: Header,
body: Body,
}

impl From<Attestation> for AttestationUnchecked {
fn from(attestation: Attestation) -> Self {
Self {
body: attestation.body,
header: attestation.header,
signature: attestation.signature,
}
}
}

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

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

#[test]
fn test_validation_ok() {
let (attestation, provider) = basic_attestation_fixture();
let unchecked: AttestationUnchecked = attestation.into();
let result = Attestation::try_from_unchecked(unchecked, &provider);
assert!(result.is_ok());
}

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

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

let unchecked: AttestationUnchecked = attestation.into();
let result = Attestation::try_from_unchecked(unchecked, &provider);
assert!(result.is_err());
}
}
34 changes: 30 additions & 4 deletions 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 @@ -222,13 +223,13 @@ pub fn request_fixture(
}
}

/// Returns an attestation fixture for testing.
/// Returns an attestation fixture and a crypto provider for testing.
pub fn attestation_fixture(
request: Request,
connection: ConnectionFixture,
signature_alg: SignatureAlgId,
secret: EncoderSecret,
) -> Attestation {
) -> (Attestation, CryptoProvider) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to couple the attestation fixture to a provider. They can be constructed separately.

let ConnectionFixture {
connection_info,
server_cert_data,
Expand Down Expand Up @@ -261,5 +262,30 @@ pub fn attestation_fixture(
.server_ephemeral_key(server_ephemeral_key)
.encoder_secret(secret);

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

/// Returns a basic attestation fixture and a crypto provider for testing.
pub fn basic_attestation_fixture() -> (Attestation, CryptoProvider) {
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(),
)
}
10 changes: 5 additions & 5 deletions crates/core/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ mod test {
Vec::new(),
);

let attestation = attestation_fixture(
let (attestation, _) = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
Expand All @@ -152,7 +152,7 @@ mod test {
Vec::new(),
);

let attestation = attestation_fixture(
let (attestation, _) = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
Expand All @@ -178,7 +178,7 @@ mod test {
Vec::new(),
);

let attestation = attestation_fixture(
let (attestation, _) = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
Expand All @@ -204,7 +204,7 @@ mod test {
Vec::new(),
);

let attestation = attestation_fixture(
let (attestation, _) = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
Expand Down Expand Up @@ -240,7 +240,7 @@ mod test {
Vec::new(),
);

let attestation = attestation_fixture(
let (attestation, _) = attestation_fixture(
request.clone(),
connection,
SignatureAlgId::SECP256K1,
Expand Down
3 changes: 1 addition & 2 deletions crates/core/src/transcript/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,14 +507,13 @@ mod tests {
let transcript_proof = builder.build().unwrap();

request.encoding_commitment_root = None;
let attestation = attestation_fixture(
let (attestation, provider) = attestation_fixture(
request,
connection,
SignatureAlgId::SECP256K1,
encoder_secret(),
);

let provider = CryptoProvider::default();
let err = transcript_proof
.verify_with_provider(&provider, &attestation.body)
.err()
Expand Down
6 changes: 4 additions & 2 deletions crates/prover/src/notarize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::{state::Notarize, Prover, ProverError};
use serio::{stream::IoStreamExt as _, SinkExt as _};
use tlsn_common::encoding;
use tlsn_core::{
attestation::Attestation,
attestation::{Attestation, AttestationUnchecked},
request::{Request, RequestConfig},
transcript::{encoding::EncodingTree, Transcript, TranscriptCommitConfig},
Secrets,
Expand Down Expand Up @@ -95,7 +95,9 @@ impl Prover<Notarize> {

ctx.io_mut().send(request.clone()).await?;

let attestation: Attestation = ctx.io_mut().expect_next().await?;
let unchecked: AttestationUnchecked = ctx.io_mut().expect_next().await?;
let attestation = Attestation::try_from_unchecked(unchecked, provider)
.map_err(ProverError::attestation)?;

Ok::<_, ProverError>(attestation)
})
Expand Down
Loading