use std::io;
use aws_lc_rs::hmac;
use aws_lc_rs::signature::{ED25519, UnparsedPublicKey};
use subtle::ConstantTimeEq;
use x509_parser::certificate::X509Certificate;
use x509_parser::prelude::FromDer;
#[inline]
pub fn extract_certificate_der(certificate_message: &[u8]) -> io::Result<&[u8]> {
if certificate_message.len() < 4 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Certificate message too short",
));
}
let mut pos = 4;
if pos >= certificate_message.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Certificate message truncated at context length",
));
}
let context_len = certificate_message[pos] as usize;
pos += 1 + context_len;
if pos + 3 > certificate_message.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Certificate message truncated at list length",
));
}
let _list_len = u32::from_be_bytes([
0,
certificate_message[pos],
certificate_message[pos + 1],
certificate_message[pos + 2],
]) as usize;
pos += 3;
if pos + 3 > certificate_message.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Certificate message truncated at cert length",
));
}
let cert_len = u32::from_be_bytes([
0,
certificate_message[pos],
certificate_message[pos + 1],
certificate_message[pos + 2],
]) as usize;
pos += 3;
if pos + cert_len > certificate_message.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Certificate message truncated at cert data",
));
}
Ok(&certificate_message[pos..pos + cert_len])
}
#[inline]
pub fn verify_certificate_hmac(cert_der: &[u8], auth_key: &[u8; 32]) -> io::Result<()> {
let (_, cert) = X509Certificate::from_der(cert_der).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Failed to parse X.509 certificate: {}", e),
)
})?;
let spki = cert.public_key();
let pubkey_data: &[u8] = &spki.subject_public_key.data;
let signature: &[u8] = &cert.signature_value.data;
log::debug!(
"REALITY CLIENT: Parsed certificate - pubkey len={}, sig len={}",
pubkey_data.len(),
signature.len()
);
if pubkey_data.len() != 32 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected Ed25519 public key (32 bytes), got {} bytes",
pubkey_data.len()
),
));
}
if signature.len() != 64 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected 64-byte signature for HMAC-SHA512 verification, got {} bytes",
signature.len()
),
));
}
let hmac_key = hmac::Key::new(hmac::HMAC_SHA512, auth_key);
let hmac_tag = hmac::sign(&hmac_key, pubkey_data);
let expected_signature = hmac_tag.as_ref();
log::debug!(
"REALITY CLIENT: HMAC verification - ed25519_pubkey={:02x?}",
pubkey_data
);
log::debug!(
"REALITY CLIENT: HMAC verification - expected_sig={:02x?}",
expected_signature
);
log::debug!(
"REALITY CLIENT: HMAC verification - actual_sig={:02x?}",
signature
);
if expected_signature.ct_eq(signature).unwrap_u8() == 0 {
log::warn!(
"REALITY CLIENT: Certificate HMAC mismatch - not a REALITY server (possible MITM or misconfiguration)"
);
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Certificate HMAC verification failed - not a REALITY-signed certificate",
));
}
log::debug!("REALITY CLIENT: Certificate HMAC verified successfully");
Ok(())
}
#[inline]
pub fn extract_ed25519_public_key(cert_der: &[u8]) -> io::Result<[u8; 32]> {
let (_, cert) = X509Certificate::from_der(cert_der).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Failed to parse X.509 certificate: {}", e),
)
})?;
let spki = cert.public_key();
let pubkey_data: &[u8] = &spki.subject_public_key.data;
if pubkey_data.len() != 32 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected Ed25519 public key (32 bytes), got {} bytes",
pubkey_data.len()
),
));
}
let mut public_key = [0u8; 32];
public_key.copy_from_slice(pubkey_data);
Ok(public_key)
}
#[inline]
pub fn extract_certificate_verify_signature(cert_verify_message: &[u8]) -> io::Result<Vec<u8>> {
if cert_verify_message.len() < 72 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"CertificateVerify message too short: {} bytes",
cert_verify_message.len()
),
));
}
if cert_verify_message[0] != 0x0f {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Expected CertificateVerify type (0x0f), got 0x{:02x}",
cert_verify_message[0]
),
));
}
let pos = 4;
let sig_alg = u16::from_be_bytes([cert_verify_message[pos], cert_verify_message[pos + 1]]);
if sig_alg != 0x0807 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Unsupported signature algorithm: 0x{:04x}, expected Ed25519 (0x0807)",
sig_alg
),
));
}
let sig_len =
u16::from_be_bytes([cert_verify_message[pos + 2], cert_verify_message[pos + 3]]) as usize;
if sig_len != 64 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Invalid Ed25519 signature length: {}, expected 64", sig_len),
));
}
let sig_start = pos + 4;
if sig_start + sig_len > cert_verify_message.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"CertificateVerify message truncated",
));
}
Ok(cert_verify_message[sig_start..sig_start + sig_len].to_vec())
}
#[inline]
pub fn verify_certificate_verify_signature(
public_key: &[u8; 32],
signature: &[u8],
transcript_hash: &[u8],
) -> io::Result<()> {
if signature.len() != 64 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Invalid signature length: {}, expected 64", signature.len()),
));
}
let mut signed_content = Vec::with_capacity(64 + 34 + transcript_hash.len());
signed_content.extend_from_slice(&[0x20u8; 64]); signed_content.extend_from_slice(b"TLS 1.3, server CertificateVerify");
signed_content.push(0x00);
signed_content.extend_from_slice(transcript_hash);
let public_key = UnparsedPublicKey::new(&ED25519, public_key);
public_key.verify(&signed_content, signature).map_err(|_| {
log::warn!("REALITY CLIENT: CertificateVerify signature verification failed");
io::Error::new(
io::ErrorKind::PermissionDenied,
"CertificateVerify signature verification failed",
)
})?;
log::debug!("REALITY CLIENT: CertificateVerify signature verified successfully");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use aws_lc_rs::signature::KeyPair;
#[test]
fn test_extract_certificate_der_valid() {
let cert_data = b"fake_certificate_der_data";
let cert_len = cert_data.len();
let entry_len = 3 + cert_len + 2;
let list_len = entry_len;
let body_len = 1 + 3 + entry_len;
let mut message = vec![
0x0b,
((body_len >> 16) & 0xff) as u8,
((body_len >> 8) & 0xff) as u8,
(body_len & 0xff) as u8,
0x00,
((list_len >> 16) & 0xff) as u8,
((list_len >> 8) & 0xff) as u8,
(list_len & 0xff) as u8,
((cert_len >> 16) & 0xff) as u8,
((cert_len >> 8) & 0xff) as u8,
(cert_len & 0xff) as u8,
];
message.extend_from_slice(cert_data);
message.push(0x00);
message.push(0x00);
let result = extract_certificate_der(&message);
assert!(result.is_ok());
assert_eq!(result.unwrap(), cert_data);
}
#[test]
fn test_extract_certificate_der_too_short() {
let message = vec![0x0b, 0x00, 0x00]; let result = extract_certificate_der(&message);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_extract_certificate_der_truncated_context() {
let message = vec![0x0b, 0x00, 0x00, 0x10]; let result = extract_certificate_der(&message);
assert!(result.is_err());
}
#[test]
fn test_verify_certificate_hmac_with_real_cert() {
let key_pair = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519)
.expect("Failed to generate Ed25519 key pair");
let params = rcgen::CertificateParams::new(vec!["test.example.com".to_string()])
.expect("Failed to create certificate params");
let cert = params
.self_signed(&key_pair)
.expect("Failed to create self-signed certificate");
let mut cert_der = cert.der().to_vec();
let signing_key =
aws_lc_rs::signature::Ed25519KeyPair::from_pkcs8(key_pair.serialized_der())
.expect("Failed to parse key");
let public_key_bytes = signing_key.public_key().as_ref();
let (_, parsed_cert) =
X509Certificate::from_der(&cert_der).expect("Failed to parse certificate");
let sig_offset =
parsed_cert.signature_value.data.as_ptr() as usize - cert_der.as_ptr() as usize;
let auth_key = [0x42u8; 32];
let hmac_key = hmac::Key::new(hmac::HMAC_SHA512, &auth_key);
let hmac_tag = hmac::sign(&hmac_key, public_key_bytes);
cert_der[sig_offset..sig_offset + 64].copy_from_slice(hmac_tag.as_ref());
let result = verify_certificate_hmac(&cert_der, &auth_key);
assert!(result.is_ok(), "HMAC verification should succeed");
}
#[test]
fn test_verify_certificate_hmac_invalid_signature() {
let key_pair = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519)
.expect("Failed to generate Ed25519 key pair");
let params = rcgen::CertificateParams::new(vec!["test.example.com".to_string()])
.expect("Failed to create certificate params");
let cert = params
.self_signed(&key_pair)
.expect("Failed to create self-signed certificate");
let cert_der = cert.der().to_vec();
let auth_key = [0x42u8; 32];
let result = verify_certificate_hmac(&cert_der, &auth_key);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::PermissionDenied);
}
#[test]
fn test_verify_certificate_hmac_invalid_der() {
let auth_key = [0x11u8; 32];
let invalid_der = vec![0x30, 0x00];
let result = verify_certificate_hmac(&invalid_der, &auth_key);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_extract_ed25519_public_key() {
let key_pair = rcgen::KeyPair::generate_for(&rcgen::PKCS_ED25519)
.expect("Failed to generate Ed25519 key pair");
let params = rcgen::CertificateParams::new(vec!["test.example.com".to_string()])
.expect("Failed to create certificate params");
let cert = params
.self_signed(&key_pair)
.expect("Failed to create self-signed certificate");
let cert_der = cert.der().to_vec();
let result = extract_ed25519_public_key(&cert_der);
assert!(result.is_ok());
let public_key = result.unwrap();
assert_eq!(public_key.len(), 32);
let signing_key =
aws_lc_rs::signature::Ed25519KeyPair::from_pkcs8(key_pair.serialized_der())
.expect("Failed to parse key");
assert_eq!(public_key, signing_key.public_key().as_ref());
}
#[test]
fn test_extract_certificate_verify_signature() {
let signature = [0xABu8; 64];
let payload_len = 2 + 2 + 64;
let mut message = vec![0x0f, 0x00, 0x00, payload_len as u8, 0x08, 0x07, 0x00, 0x40];
message.extend_from_slice(&signature);
let result = extract_certificate_verify_signature(&message);
assert!(result.is_ok());
assert_eq!(result.unwrap(), signature.to_vec());
}
#[test]
fn test_extract_certificate_verify_wrong_type() {
let mut message = vec![0x0b; 72]; message[4] = 0x08;
message[5] = 0x07;
message[6] = 0x00;
message[7] = 0x40;
let result = extract_certificate_verify_signature(&message);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Expected CertificateVerify type")
);
}
#[test]
fn test_extract_certificate_verify_unsupported_algorithm() {
let mut message = vec![0x00; 72];
message[0] = 0x0f; message[4] = 0x04; message[5] = 0x01;
let result = extract_certificate_verify_signature(&message);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Unsupported signature algorithm")
);
}
#[test]
fn test_extract_certificate_verify_too_short() {
let message = vec![0x0f; 10];
let result = extract_certificate_verify_signature(&message);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("too short"));
}
#[test]
fn test_verify_certificate_verify_signature_valid() {
let key_pair = aws_lc_rs::signature::Ed25519KeyPair::generate()
.expect("Failed to generate Ed25519 key pair");
let public_key: [u8; 32] = key_pair.public_key().as_ref().try_into().unwrap();
let transcript_hash = [0x42u8; 32];
let mut signed_content = Vec::new();
signed_content.extend_from_slice(&[0x20u8; 64]); signed_content.extend_from_slice(b"TLS 1.3, server CertificateVerify");
signed_content.push(0x00);
signed_content.extend_from_slice(&transcript_hash);
let signature = key_pair.sign(&signed_content);
let result =
verify_certificate_verify_signature(&public_key, signature.as_ref(), &transcript_hash);
assert!(result.is_ok(), "Signature verification should succeed");
}
#[test]
fn test_verify_certificate_verify_signature_wrong_key() {
let key_pair1 = aws_lc_rs::signature::Ed25519KeyPair::generate()
.expect("Failed to generate Ed25519 key pair");
let key_pair2 = aws_lc_rs::signature::Ed25519KeyPair::generate()
.expect("Failed to generate Ed25519 key pair");
let public_key2: [u8; 32] = key_pair2.public_key().as_ref().try_into().unwrap();
let transcript_hash = [0x42u8; 32];
let mut signed_content = Vec::new();
signed_content.extend_from_slice(&[0x20u8; 64]);
signed_content.extend_from_slice(b"TLS 1.3, server CertificateVerify");
signed_content.push(0x00);
signed_content.extend_from_slice(&transcript_hash);
let signature = key_pair1.sign(&signed_content);
let result =
verify_certificate_verify_signature(&public_key2, signature.as_ref(), &transcript_hash);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::PermissionDenied);
}
#[test]
fn test_verify_certificate_verify_signature_wrong_transcript() {
let key_pair = aws_lc_rs::signature::Ed25519KeyPair::generate()
.expect("Failed to generate Ed25519 key pair");
let public_key: [u8; 32] = key_pair.public_key().as_ref().try_into().unwrap();
let transcript_hash1 = [0x42u8; 32];
let transcript_hash2 = [0x43u8; 32];
let mut signed_content = Vec::new();
signed_content.extend_from_slice(&[0x20u8; 64]);
signed_content.extend_from_slice(b"TLS 1.3, server CertificateVerify");
signed_content.push(0x00);
signed_content.extend_from_slice(&transcript_hash1);
let signature = key_pair.sign(&signed_content);
let result =
verify_certificate_verify_signature(&public_key, signature.as_ref(), &transcript_hash2);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::PermissionDenied);
}
#[test]
fn test_verify_certificate_verify_signature_invalid_length() {
let public_key = [0x00u8; 32];
let transcript_hash = [0x42u8; 32];
let invalid_signature = [0x00u8; 32];
let result =
verify_certificate_verify_signature(&public_key, &invalid_signature, &transcript_hash);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData);
}
#[test]
fn test_certificate_verify_end_to_end() {
use aws_lc_rs::digest;
let key_pair = aws_lc_rs::signature::Ed25519KeyPair::generate()
.expect("Failed to generate Ed25519 key pair");
let public_key: [u8; 32] = key_pair.public_key().as_ref().try_into().unwrap();
let fake_client_hello = b"ClientHello data here";
let fake_server_hello = b"ServerHello data here";
let fake_encrypted_extensions = b"EncryptedExtensions here";
let fake_certificate = b"Certificate message here";
let mut transcript = digest::Context::new(&digest::SHA256);
transcript.update(fake_client_hello);
transcript.update(fake_server_hello);
transcript.update(fake_encrypted_extensions);
transcript.update(fake_certificate);
let transcript_hash = transcript.finish();
let mut signed_content = Vec::new();
signed_content.extend_from_slice(&[0x20u8; 64]);
signed_content.extend_from_slice(b"TLS 1.3, server CertificateVerify");
signed_content.push(0x00);
signed_content.extend_from_slice(transcript_hash.as_ref());
let signature = key_pair.sign(&signed_content);
let payload_len = 2 + 2 + 64;
let mut cv_message = vec![0x0f, 0x00, 0x00, payload_len as u8, 0x08, 0x07, 0x00, 0x40];
cv_message.extend_from_slice(signature.as_ref());
let extracted_sig =
extract_certificate_verify_signature(&cv_message).expect("Failed to extract signature");
let result = verify_certificate_verify_signature(
&public_key,
&extracted_sig,
transcript_hash.as_ref(),
);
assert!(
result.is_ok(),
"End-to-end CertificateVerify verification should succeed"
);
}
}