Skip to content

Commit f32cfba

Browse files
Jiale Zhangjialez0
authored andcommitted
verifier: fix Hygon TPM SM2 quote signature verification
The previous verify path built an SM2 PKey and ran the signature through OpenSSL's EVP_DigestVerify (effectively `Verifier::new(SM3, pkey)`). That path either applies the GB/T 32918 ZA pre-processing (SM3(ENTL || ID || a || b || xG || yG || xA || yA) prepended to the message) or, on a plain EC PKey, silently dispatches to ECDSA verify. Neither matches what TPM2 produces: per the TPM 2.0 spec the SM2 quote signs SM3(attestBody) directly, with no ZA mixing and no DER wrapping. End-to-end attestation against a real Hygon TPM therefore failed with "Verify Hygon TPM quote signature failed" even when the AK, evidence body, and signature were all correct. Drop the OpenSSL high-level verifier and implement the SM2 verify equation from GB/T 32918.2-2016 §7.1 directly using BigNum/EcPoint: e = SM3(attestBody) t = (r + s) mod n with t != 0 P = s * G + t * Q ok iff (e + P.x) mod n == r Range-check r and s against [1, n-1] up front and reject t == 0 to match the spec. Reuse the existing extracted (r, s) from the marshalled TPM signature; create_sm2_pkey is left in place because it is still used to compare AK pubkeys against the keylime registrar. Signed-off-by: Jiale Zhang <xinjian.zjl@alibaba-inc.com>
1 parent 312088a commit f32cfba

1 file changed

Lines changed: 46 additions & 15 deletions

File tree

  • deps/verifier/src/hygon_tpm

deps/verifier/src/hygon_tpm/mod.rs

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@ use eventlog_rs::{BiosEventlog, Eventlog};
1010
use log::{debug, info};
1111
use openssl::bn::{BigNum, BigNumContext};
1212
use openssl::ec::{EcGroup, EcKey, EcPoint};
13-
use openssl::ecdsa::EcdsaSig;
14-
use openssl::hash::MessageDigest;
1513
use openssl::nid::Nid;
1614
use openssl::pkey::PKey;
17-
use openssl::sign::Verifier as OpenSslVerifier;
1815
use openssl::x509::X509;
1916
use reqwest::Client;
2017
use serde::{Deserialize, Serialize};
@@ -94,7 +91,7 @@ fn create_sm2_pkey(ak_pubkey: &HygonSm2PublicKey) -> Result<PKey<openssl::pkey::
9491
Ok(PKey::from_ec_key(ec_key)?)
9592
}
9693

97-
fn signature_to_der(sig_b64: &str) -> Result<Vec<u8>> {
94+
fn extract_sm2_signature_components(sig_b64: &str) -> Result<(BigNum, BigNum)> {
9895
let sig_bytes = base64::engine::general_purpose::STANDARD.decode(sig_b64)?;
9996
let signature = Signature::unmarshall(&sig_bytes)?;
10097
let Signature::Sm2(sm2_sig) = signature else {
@@ -103,8 +100,7 @@ fn signature_to_der(sig_b64: &str) -> Result<Vec<u8>> {
103100

104101
let r = BigNum::from_slice(sm2_sig.signature_r().value())?;
105102
let s = BigNum::from_slice(sm2_sig.signature_s().value())?;
106-
let sig = EcdsaSig::from_private_components(r, s)?;
107-
Ok(sig.to_der()?)
103+
Ok((r, s))
108104
}
109105

110106
async fn verify_registrar_binding(evidence: &HygonTpmEvidence, uuid: &str) -> Result<()> {
@@ -420,17 +416,52 @@ fn parse_measurements_from_event(
420416

421417
impl HygonTpmQuote {
422418
fn verify_signature(&self, ak_pubkey: &HygonSm2PublicKey) -> Result<()> {
423-
let pkey = create_sm2_pkey(ak_pubkey)?;
424-
let signature_der = signature_to_der(&self.attest_sig)?;
419+
let (sig_r, sig_s) = extract_sm2_signature_components(&self.attest_sig)?;
425420
let attest_body = base64::engine::general_purpose::STANDARD.decode(&self.attest_body)?;
426421

427-
// Reuse OpenSSL's current SM2-capable verification path that works with
428-
// the crate version in this workspace. If real hardware shows a different
429-
// distinguishing-ID requirement, we can narrow it on-device without
430-
// changing the evidence schema or policy surface.
431-
let mut verifier = OpenSslVerifier::new(MessageDigest::sm3(), &pkey)?;
432-
verifier.update(&attest_body)?;
433-
if !verifier.verify(&signature_der)? {
422+
// TPM2 SM2 quotes sign SM3(attest_body) directly per the TPM2 spec —
423+
// there is no GB/T 32918 ZA pre-processing. OpenSSL's EVP_DigestVerify
424+
// path either applies ZA (digest mismatch) or, on a plain EC PKey,
425+
// dispatches to ECDSA verify (wrong algorithm). Run the SM2 verify
426+
// equation (GB/T 32918.2 §7.1) directly against e = SM3(attest_body).
427+
let mut hasher = Sm3::new();
428+
hasher.update(&attest_body);
429+
let e_bytes = hasher.finalize();
430+
let e = BigNum::from_slice(&e_bytes)?;
431+
432+
let nid = Nid::from_raw(openssl_sys::NID_sm2);
433+
let group = EcGroup::from_curve_name(nid)?;
434+
let mut ctx = BigNumContext::new()?;
435+
let mut order = BigNum::new()?;
436+
group.order(&mut order, &mut ctx)?;
437+
438+
let one = BigNum::from_u32(1)?;
439+
if sig_r < one || sig_r >= order || sig_s < one || sig_s >= order {
440+
bail!("Verify Hygon TPM quote signature failed");
441+
}
442+
443+
let mut t = BigNum::new()?;
444+
t.mod_add(&sig_r, &sig_s, &order, &mut ctx)?;
445+
if t.num_bits() == 0 {
446+
bail!("Verify Hygon TPM quote signature failed");
447+
}
448+
449+
let bx = BigNum::from_hex_str(&ak_pubkey.x)?;
450+
let by = BigNum::from_hex_str(&ak_pubkey.y)?;
451+
let mut q = EcPoint::new(&group)?;
452+
q.set_affine_coordinates_gfp(&group, &bx, &by, &mut ctx)?;
453+
454+
let mut p = EcPoint::new(&group)?;
455+
p.mul_full(&group, &sig_s, &q, &t, &mut ctx)?;
456+
457+
let mut x1 = BigNum::new()?;
458+
let mut y1 = BigNum::new()?;
459+
p.affine_coordinates_gfp(&group, &mut x1, &mut y1, &mut ctx)?;
460+
461+
let mut r_check = BigNum::new()?;
462+
r_check.mod_add(&e, &x1, &order, &mut ctx)?;
463+
464+
if r_check != sig_r {
434465
bail!("Verify Hygon TPM quote signature failed");
435466
}
436467

0 commit comments

Comments
 (0)