Skip to content

Commit 8f7e379

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

File tree

4 files changed

+572
-0
lines changed

4 files changed

+572
-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: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
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().to_vec();
143+
if digest != quote.pcr_digest().as_bytes() {
144+
return Ok(false);
145+
}
146+
Ok(true)
147+
}
148+
149+
/// Verify a quote
150+
///
151+
/// # Arguments
152+
/// * `attest` - Attestation data containing a quote
153+
/// * `signature` - Signature for the attestation data
154+
/// * `public` - TPM2 public struct which contains the public key for verification
155+
/// * `pcr_data` - Optional pcr values to verify
156+
/// * `qualifying_data` - qualifying_data to verify
157+
///
158+
/// # Returns
159+
/// The command returns a boolean
160+
///
161+
/// # Errors
162+
/// * if the qualifying data provided is too long, a `WrongParamSize` wrapper error will be returned
163+
///
164+
/// # Examples
165+
///
166+
/// ```rust
167+
/// # use std::convert::TryFrom;
168+
/// # use tss_esapi::{
169+
/// # attributes::SessionAttributes,
170+
/// # abstraction::{ak, ek, AsymmetricAlgorithmSelection},
171+
/// # constants::SessionType, Context,
172+
/// # interface_types::{
173+
/// # algorithm::{HashingAlgorithm, SignatureSchemeAlgorithm},
174+
/// # ecc::EccCurve,
175+
/// # },
176+
/// # structures::{
177+
/// # Data, PcrSelectionListBuilder, PcrSlot,
178+
/// # SignatureScheme, SymmetricDefinition,
179+
/// # },
180+
/// # TctiNameConf,
181+
/// # utils,
182+
/// # };
183+
/// # let mut context =
184+
/// # Context::new(
185+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
186+
/// # ).expect("Failed to create Context");
187+
/// # let session = context
188+
/// # .start_auth_session(
189+
/// # None,
190+
/// # None,
191+
/// # None,
192+
/// # SessionType::Hmac,
193+
/// # SymmetricDefinition::AES_256_CFB,
194+
/// # tss_esapi::interface_types::algorithm::HashingAlgorithm::Sha256,
195+
/// # )
196+
/// # .expect("Failed to create session")
197+
/// # .expect("Received invalid handle");
198+
/// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
199+
/// # .with_decrypt(true)
200+
/// # .with_encrypt(true)
201+
/// # .build();
202+
/// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
203+
/// # .expect("Failed to set attributes on session");
204+
/// # context.set_sessions((Some(session), None, None));
205+
/// # let qualifying_data = vec![0xff; 16];
206+
/// # let ek_ecc = ek::create_ek_object(
207+
/// # &mut context,
208+
/// # AsymmetricAlgorithmSelection::Ecc(EccCurve::NistP256),
209+
/// # None,
210+
/// # )
211+
/// # .unwrap();
212+
/// # let ak_res = ak::create_ak(
213+
/// # &mut context,
214+
/// # ek_ecc,
215+
/// # HashingAlgorithm::Sha256,
216+
/// # AsymmetricAlgorithmSelection::Ecc(EccCurve::NistP256),
217+
/// # SignatureSchemeAlgorithm::EcDsa,
218+
/// # None,
219+
/// # None,
220+
/// # )
221+
/// # .unwrap();
222+
/// # let ak_ecc = ak::load_ak(
223+
/// # &mut context,
224+
/// # ek_ecc,
225+
/// # None,
226+
/// # ak_res.out_private,
227+
/// # ak_res.out_public.clone(),
228+
/// # )
229+
/// # .unwrap();
230+
/// # let pcr_selection_list = PcrSelectionListBuilder::new()
231+
/// # .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot2])
232+
/// # .build()
233+
/// # .expect("Failed to create PcrSelectionList");
234+
/// let (attest, signature) = context
235+
/// .quote(
236+
/// ak_ecc,
237+
/// Data::try_from(qualifying_data.clone()).unwrap(),
238+
/// SignatureScheme::Null,
239+
/// pcr_selection_list.clone(),
240+
/// )
241+
/// .expect("Failed to get a quote");
242+
/// let (_update_counter, pcr_sel, pcr_data) = context
243+
/// .execute_without_session(|ctx| ctx.pcr_read(pcr_selection_list))
244+
/// .unwrap();
245+
/// let public = ak_res.out_public;
246+
/// utils::checkquote(
247+
/// &attest,
248+
/// &signature,
249+
/// &public,
250+
/// &Some((pcr_sel.clone(), pcr_data.clone())),
251+
/// &qualifying_data
252+
/// )
253+
/// .unwrap();
254+
/// ```
255+
pub fn checkquote(
256+
attest: &Attest,
257+
signature: &Signature,
258+
public: &Public,
259+
pcr_data: &Option<(PcrSelectionList, DigestList)>,
260+
qualifying_data: &Vec<u8>,
261+
) -> Result<bool> {
262+
let quote = match attest.attested() {
263+
AttestInfo::Quote { info } => info,
264+
_ => {
265+
return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
266+
}
267+
};
268+
let bytes = attest.marshall()?;
269+
let hash_alg = match signature {
270+
#[cfg(feature = "p256")]
271+
Signature::EcDsa(sig) => {
272+
if !verify_p256(&public, &bytes, &sig)? {
273+
return Ok(false);
274+
}
275+
sig.hashing_algorithm()
276+
}
277+
#[cfg(feature = "rsa")]
278+
Signature::RsaPss(sig) => {
279+
if !verify_rsa(&public, &bytes, &sig)? {
280+
return Ok(false);
281+
}
282+
sig.hashing_algorithm()
283+
}
284+
_ => {
285+
return Err(Error::WrapperError(WrapperErrorKind::UnsupportedParam));
286+
}
287+
};
288+
if qualifying_data != attest.extra_data().as_bytes() {
289+
return Ok(false);
290+
}
291+
if let Some((selections, digests)) = pcr_data {
292+
if !checkquote_pcr_digests(quote, selections, digests, hash_alg)? {
293+
return Ok(false);
294+
}
295+
}
296+
Ok(true)
297+
}
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)