Skip to content

Commit 0a29ecd

Browse files
committed
Add checkquote function without using the tpm2
Signed-off-by: Simon Brand <simon.brand@postadigitale.de>
1 parent 78c0aed commit 0a29ecd

File tree

4 files changed

+544
-0
lines changed

4 files changed

+544
-0
lines changed

tss-esapi/src/utils/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ use crate::{Context, Error, Result, WrapperErrorKind};
2323
use std::convert::TryFrom;
2424
use zeroize::Zeroize;
2525

26+
#[cfg(all(
27+
any(feature = "p256", feature = "rsa",),
28+
any(feature = "sha1", feature = "sha2",)
29+
))]
30+
mod quote;
31+
#[cfg(all(
32+
any(feature = "p256", feature = "rsa",),
33+
any(feature = "sha1", feature = "sha2",)
34+
))]
35+
pub use quote::checkquote;
36+
2637
/// Create the [Public] structure for a restricted decryption key.
2738
///
2839
/// * `symmetric` - Cipher to be used for decrypting children of the key

tss-esapi/src/utils/quote.rs

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
use crate::error::Error;
4+
use crate::error::Result;
5+
use crate::WrapperErrorKind;
6+
use crate::{
7+
interface_types::algorithm::HashingAlgorithm,
8+
structures::{Attest, AttestInfo, DigestList, PcrSelectionList, Public, QuoteInfo, Signature},
9+
traits::Marshall,
10+
utils::PublicKey,
11+
};
12+
use digest::{Digest, DynDigest};
13+
14+
#[cfg(feature = "p256")]
15+
use crate::structures::EccSignature;
16+
#[cfg(feature = "p256")]
17+
use p256::ecdsa::{Signature as SignatureP256, VerifyingKey};
18+
#[cfg(feature = "p256")]
19+
use signature::{hazmat::PrehashVerifier, Verifier};
20+
21+
#[cfg(feature = "rsa")]
22+
use crate::structures::RsaSignature;
23+
#[cfg(feature = "rsa")]
24+
use rsa::{pss::Pss, RsaPublicKey};
25+
26+
#[cfg(feature = "p256")]
27+
fn verify_p256(public: &Public, message: &[u8], signature: &EccSignature) -> Result<bool> {
28+
let public_key = PublicKey::try_from(public.clone())?;
29+
let (x, y) = match public_key {
30+
PublicKey::Ecc { x, y } => (x, y),
31+
_ => {
32+
return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
33+
}
34+
};
35+
let mut sec1_bytes = Vec::<u8>::with_capacity(1 + x.len() + y.len());
36+
sec1_bytes.push(0x04);
37+
sec1_bytes.extend_from_slice(&x);
38+
sec1_bytes.extend_from_slice(&y);
39+
let verifying_key = match VerifyingKey::from_sec1_bytes(&sec1_bytes) {
40+
Ok(s) => s,
41+
Err(_) => {
42+
return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
43+
}
44+
};
45+
46+
let mut sig_bytes = Vec::with_capacity(64);
47+
sig_bytes.extend_from_slice(signature.signature_r().as_ref());
48+
sig_bytes.extend_from_slice(signature.signature_s().as_ref());
49+
let generic_sig = digest::generic_array::GenericArray::clone_from_slice(&sig_bytes);
50+
let sig = match SignatureP256::from_bytes(&generic_sig) {
51+
Ok(s) => s,
52+
Err(_) => {
53+
return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
54+
}
55+
};
56+
57+
let verify_result = match signature.hashing_algorithm() {
58+
#[cfg(feature = "sha1")]
59+
HashingAlgorithm::Sha1 => {
60+
let mut hasher = sha1::Sha1::new();
61+
Digest::update(&mut hasher, &message);
62+
verifying_key.verify_prehash(&hasher.finalize(), &sig)
63+
}
64+
#[cfg(feature = "sha2")]
65+
HashingAlgorithm::Sha256 => verifying_key.verify(&message, &sig),
66+
_ => {
67+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
68+
}
69+
};
70+
return Ok(match verify_result {
71+
Ok(_) => true,
72+
Err(_) => false,
73+
});
74+
}
75+
76+
#[cfg(feature = "rsa")]
77+
fn verify_rsa(public: &Public, message: &[u8], signature: &RsaSignature) -> Result<bool> {
78+
let public_key = PublicKey::try_from(public.clone())?;
79+
let rsa_key = RsaPublicKey::try_from(&public_key)?;
80+
let sig = signature.signature();
81+
let mut hasher: Box<dyn DynDigest> = match signature.hashing_algorithm() {
82+
#[cfg(feature = "sha1")]
83+
HashingAlgorithm::Sha1 => Box::new(sha1::Sha1::new()),
84+
#[cfg(feature = "sha2")]
85+
HashingAlgorithm::Sha256 => Box::new(sha2::Sha256::new()),
86+
_ => {
87+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
88+
}
89+
};
90+
hasher.update(&message);
91+
let hash = hasher.finalize().to_vec();
92+
93+
let scheme = match signature.hashing_algorithm() {
94+
#[cfg(feature = "sha1")]
95+
HashingAlgorithm::Sha1 => Pss::new::<sha1::Sha1>(),
96+
#[cfg(feature = "sha2")]
97+
HashingAlgorithm::Sha256 => Pss::new::<sha2::Sha256>(),
98+
_ => {
99+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
100+
}
101+
};
102+
return Ok(match rsa_key.verify(scheme, &hash, &sig) {
103+
Ok(_) => true,
104+
Err(_) => false,
105+
});
106+
}
107+
108+
fn checkquote_pcr_digests(
109+
quote: &QuoteInfo,
110+
selections: &PcrSelectionList,
111+
digests: &DigestList,
112+
hash_alg: HashingAlgorithm,
113+
) -> Result<bool> {
114+
if selections != quote.pcr_selection() {
115+
return Ok(false);
116+
}
117+
let digests_val = digests.value();
118+
let mut digest_pos = 0;
119+
let mut hasher: Box<dyn DynDigest> = match hash_alg {
120+
#[cfg(feature = "sha1")]
121+
HashingAlgorithm::Sha1 => Box::new(sha1::Sha1::new()),
122+
#[cfg(feature = "sha2")]
123+
HashingAlgorithm::Sha256 => Box::new(sha2::Sha256::new()),
124+
_ => {
125+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
126+
}
127+
};
128+
129+
for selection in selections.get_selections() {
130+
let sel_count = selection.selected().len();
131+
if digest_pos + sel_count > digests.len() {
132+
return Err(Error::WrapperError(WrapperErrorKind::WrongParamSize));
133+
}
134+
for _ in 0..sel_count {
135+
hasher.update(&digests_val[digest_pos]);
136+
digest_pos += 1;
137+
}
138+
}
139+
if digest_pos != digests.len() {
140+
return Err(Error::WrapperError(WrapperErrorKind::WrongParamSize));
141+
}
142+
let digest = hasher.finalize();
143+
Ok(digest.as_ref() == quote.pcr_digest().as_ref())
144+
}
145+
146+
/// Verify a quote
147+
///
148+
/// # Arguments
149+
/// * `attest` - Attestation data containing a quote
150+
/// * `signature` - Signature for the attestation data
151+
/// * `public` - TPM2 public struct which contains the public key for verification
152+
/// * `pcr_data` - Optional pcr values to verify
153+
/// * `qualifying_data` - qualifying data to verify
154+
///
155+
/// # Returns
156+
/// The command returns `true` if the quote is valid or `false` otherwise.
157+
///
158+
/// # Errors
159+
/// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned
160+
///
161+
/// # Examples
162+
///
163+
/// ```rust
164+
/// # use std::convert::TryFrom;
165+
/// # use tss_esapi::{
166+
/// # attributes::SessionAttributes,
167+
/// # abstraction::{ak, ek, AsymmetricAlgorithmSelection},
168+
/// # constants::SessionType, Context,
169+
/// # interface_types::{
170+
/// # algorithm::{HashingAlgorithm, SignatureSchemeAlgorithm},
171+
/// # ecc::EccCurve,
172+
/// # },
173+
/// # structures::{
174+
/// # Data, PcrSelectionListBuilder, PcrSlot,
175+
/// # SignatureScheme, SymmetricDefinition,
176+
/// # },
177+
/// # TctiNameConf,
178+
/// # utils,
179+
/// # };
180+
/// # let mut context =
181+
/// # Context::new(
182+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
183+
/// # ).expect("Failed to create Context");
184+
/// # let session = context
185+
/// # .start_auth_session(
186+
/// # None,
187+
/// # None,
188+
/// # None,
189+
/// # SessionType::Hmac,
190+
/// # SymmetricDefinition::AES_256_CFB,
191+
/// # tss_esapi::interface_types::algorithm::HashingAlgorithm::Sha256,
192+
/// # )
193+
/// # .expect("Failed to create session")
194+
/// # .expect("Received invalid handle");
195+
/// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
196+
/// # .with_decrypt(true)
197+
/// # .with_encrypt(true)
198+
/// # .build();
199+
/// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
200+
/// # .expect("Failed to set attributes on session");
201+
/// # context.set_sessions((Some(session), None, None));
202+
/// # let qualifying_data = vec![0xff; 16];
203+
/// # let ek_ecc = ek::create_ek_object(
204+
/// # &mut context,
205+
/// # AsymmetricAlgorithmSelection::Ecc(EccCurve::NistP256),
206+
/// # None,
207+
/// # )
208+
/// # .unwrap();
209+
/// # let ak_res = ak::create_ak(
210+
/// # &mut context,
211+
/// # ek_ecc,
212+
/// # HashingAlgorithm::Sha256,
213+
/// # AsymmetricAlgorithmSelection::Ecc(EccCurve::NistP256),
214+
/// # SignatureSchemeAlgorithm::EcDsa,
215+
/// # None,
216+
/// # None,
217+
/// # )
218+
/// # .unwrap();
219+
/// # let ak_ecc = ak::load_ak(
220+
/// # &mut context,
221+
/// # ek_ecc,
222+
/// # None,
223+
/// # ak_res.out_private,
224+
/// # ak_res.out_public.clone(),
225+
/// # )
226+
/// # .unwrap();
227+
/// # let pcr_selection_list = PcrSelectionListBuilder::new()
228+
/// # .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot2])
229+
/// # .build()
230+
/// # .expect("Failed to create PcrSelectionList");
231+
/// let (attest, signature) = context
232+
/// .quote(
233+
/// ak_ecc,
234+
/// Data::try_from(qualifying_data.clone()).unwrap(),
235+
/// SignatureScheme::Null,
236+
/// pcr_selection_list.clone(),
237+
/// )
238+
/// .expect("Failed to get a quote");
239+
/// let (_update_counter, pcr_sel, pcr_data) = context
240+
/// .execute_without_session(|ctx| ctx.pcr_read(pcr_selection_list))
241+
/// .unwrap();
242+
/// let public = ak_res.out_public;
243+
/// utils::checkquote(
244+
/// &attest,
245+
/// &signature,
246+
/// &public,
247+
/// &Some((pcr_sel.clone(), pcr_data.clone())),
248+
/// &qualifying_data
249+
/// )
250+
/// .unwrap();
251+
/// ```
252+
pub fn checkquote(
253+
attest: &Attest,
254+
signature: &Signature,
255+
public: &Public,
256+
pcr_data: &Option<(PcrSelectionList, DigestList)>,
257+
qualifying_data: &Vec<u8>,
258+
) -> Result<bool> {
259+
let quote = if let AttestInfo::Quote { info } = attest.attested() {
260+
info
261+
} else {
262+
return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
263+
};
264+
let bytes = attest.marshall()?;
265+
let hash_alg = match signature {
266+
#[cfg(feature = "p256")]
267+
Signature::EcDsa(sig) => {
268+
if !verify_p256(&public, &bytes, &sig)? {
269+
return Ok(false);
270+
}
271+
sig.hashing_algorithm()
272+
}
273+
#[cfg(feature = "rsa")]
274+
Signature::RsaPss(sig) => {
275+
if !verify_rsa(&public, &bytes, &sig)? {
276+
return Ok(false);
277+
}
278+
sig.hashing_algorithm()
279+
}
280+
_ => {
281+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
282+
}
283+
};
284+
if qualifying_data != attest.extra_data().as_bytes() {
285+
return Ok(false);
286+
}
287+
if let Some((selections, digests)) = pcr_data {
288+
if !checkquote_pcr_digests(quote, selections, digests, hash_alg)? {
289+
return Ok(false);
290+
}
291+
}
292+
Ok(true)
293+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// Copyright 2021 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
33
mod get_tpm_vendor_test;
4+
mod quote_test;

0 commit comments

Comments
 (0)