21e7e65b32
新增功能: - docs/ 目录:GitHub Pages 静态页面 - index.html:项目主页 - og-image.png/svg:社交分享图片 - tests/rustls_provider.rs:rustls CryptoProvider 集成测试 - rustls_provider 模块增强: - hash.rs:新增测试和文档 - hmac.rs:新增测试和文档 - kx.rs:新增测试和文档 - sign.rs:新增测试和文档 - verify.rs:新增测试和文档 CI 修复: - rustls 依赖改为 git 源,避免本地路径依赖问题 - 简化 SM3 HMAC 流式测试,移除 alloc 依赖 - 移除未使用的 extern crate alloc - 代码格式化 文档更新: - README.md:添加 GitHub Pages 链接 - CHANGELOG.md:更新变更记录 - Cargo.toml:添加 dev-dependencies
334 lines
12 KiB
Rust
334 lines
12 KiB
Rust
//! rustls CryptoProvider 端到端集成测试(阶段 L.2)
|
||
//!
|
||
//! 使用 Raw Public Key(RFC 7250)模式,无需 X.509 证书,
|
||
//! 直接用 SM2 密钥对作为身份凭证完成 TLS 1.3 握手自回环测试。
|
||
|
||
#![cfg(feature = "rustls-provider")]
|
||
|
||
use std::io::{Read, Write};
|
||
use std::sync::Arc;
|
||
|
||
use pki_types::{PrivateSec1KeyDer, ServerName, SubjectPublicKeyInfoDer};
|
||
use rustls::client::danger::{
|
||
HandshakeSignatureValid, PeerVerified, ServerIdentity, ServerVerifier,
|
||
};
|
||
use rustls::crypto::{CipherSuite, CryptoProvider, SignatureScheme};
|
||
use rustls::enums::CertificateType;
|
||
use rustls::server::danger::SignatureVerificationInput;
|
||
use rustls::{ClientConfig, ClientConnection, Connection, ServerConfig, ServerConnection};
|
||
|
||
use libsmx::rustls_provider;
|
||
use libsmx::sm2::{
|
||
der::{public_key_to_spki_der, sig_from_der},
|
||
generate_keypair, verify_message, DEFAULT_ID,
|
||
};
|
||
|
||
// ── 测试用 RNG ────────────────────────────────────────────────────────────────
|
||
|
||
struct TestRng;
|
||
|
||
impl rand_core::RngCore for TestRng {
|
||
fn next_u32(&mut self) -> u32 {
|
||
let mut b = [0u8; 4];
|
||
getrandom::getrandom(&mut b).unwrap();
|
||
u32::from_le_bytes(b)
|
||
}
|
||
fn next_u64(&mut self) -> u64 {
|
||
let mut b = [0u8; 8];
|
||
getrandom::getrandom(&mut b).unwrap();
|
||
u64::from_le_bytes(b)
|
||
}
|
||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||
getrandom::getrandom(dest).unwrap();
|
||
}
|
||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||
getrandom::getrandom(dest).map_err(|_| {
|
||
use core::num::NonZeroU32;
|
||
rand_core::Error::from(NonZeroU32::new(1).unwrap())
|
||
})
|
||
}
|
||
}
|
||
|
||
// ── 测试工具 ──────────────────────────────────────────────────────────────────
|
||
|
||
/// 生成 SM2 密钥对,返回 (SEC1 DER 私钥, SPKI DER 公钥)
|
||
fn make_sm2_keypair() -> (Vec<u8>, Vec<u8>) {
|
||
let mut rng = TestRng;
|
||
let (pri_key, pub_key) = generate_keypair(&mut rng);
|
||
|
||
// 最小 SEC1 DER: SEQUENCE { INTEGER(1), OCTET STRING(32B) }
|
||
let mut sec1 = Vec::with_capacity(39);
|
||
sec1.extend_from_slice(&[
|
||
0x30, 0x25, // SEQUENCE, length 37
|
||
0x02, 0x01, 0x01, // INTEGER(1) version
|
||
0x04, 0x20, // OCTET STRING, length 32
|
||
]);
|
||
sec1.extend_from_slice(pri_key.as_bytes());
|
||
|
||
let spki = public_key_to_spki_der(&pub_key);
|
||
(sec1, spki)
|
||
}
|
||
|
||
/// 在内存中传输 TLS 记录(left → right)
|
||
fn transfer(left: &mut impl Connection, right: &mut impl Connection) -> usize {
|
||
let mut buf = [0u8; 65536];
|
||
let mut total = 0;
|
||
while left.wants_write() {
|
||
let n = left.write_tls(&mut &mut buf[..]).unwrap();
|
||
if n == 0 {
|
||
break;
|
||
}
|
||
total += n;
|
||
let mut offs = 0;
|
||
while offs < n {
|
||
offs += right.read_tls(&mut &buf[offs..n]).unwrap();
|
||
}
|
||
}
|
||
total
|
||
}
|
||
|
||
/// 驱动完整 TLS 握手
|
||
fn do_handshake(client: &mut ClientConnection, server: &mut ServerConnection) {
|
||
while client.is_handshaking() || server.is_handshaking() {
|
||
transfer(client, server);
|
||
server.process_new_packets().unwrap();
|
||
transfer(server, client);
|
||
client.process_new_packets().unwrap();
|
||
}
|
||
}
|
||
|
||
// ── 自定义 ServerVerifier(测试用,接受任意 SM2 Raw Public Key)──────────────
|
||
|
||
#[derive(Debug)]
|
||
struct AcceptAllSm2ServerVerifier;
|
||
|
||
impl AcceptAllSm2ServerVerifier {
|
||
fn new() -> Self {
|
||
Self
|
||
}
|
||
}
|
||
|
||
impl ServerVerifier for AcceptAllSm2ServerVerifier {
|
||
fn verify_identity(
|
||
&self,
|
||
_identity: &ServerIdentity<'_>,
|
||
) -> Result<PeerVerified, rustls::Error> {
|
||
// Reason: 测试中不验证服务端身份(Raw Public Key 模式,无 CA 信任链)
|
||
Ok(PeerVerified::assertion())
|
||
}
|
||
|
||
fn verify_tls12_signature(
|
||
&self,
|
||
_input: &SignatureVerificationInput<'_>,
|
||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||
Err(rustls::Error::General("TLS 1.2 not supported".into()))
|
||
}
|
||
|
||
fn verify_tls13_signature(
|
||
&self,
|
||
input: &SignatureVerificationInput<'_>,
|
||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||
// Reason: webpki 不支持 SM2 OID,直接调用 libsmx SM2 验签实现,
|
||
// 绕过 webpki 的 OID 匹配逻辑
|
||
use rustls::SignerPublicKey;
|
||
let spki_der: &[u8] = match input.signer {
|
||
SignerPublicKey::RawPublicKey(spki) => spki.as_ref(),
|
||
_ => return Err(rustls::Error::General("expected Raw Public Key".into())),
|
||
};
|
||
// SM2 SPKI 结构(91字节):
|
||
// SEQUENCE(2B) + AlgId SEQUENCE(21B) + BIT STRING tag+len+pad(3B) + 公钥(65B)
|
||
// 公钥从偏移 26 开始,共 65 字节
|
||
if spki_der.len() != 91 {
|
||
return Err(rustls::Error::General(format!(
|
||
"unexpected SPKI length: {}",
|
||
spki_der.len()
|
||
)));
|
||
}
|
||
let pub_key_bytes: &[u8; 65] = spki_der[26..91]
|
||
.try_into()
|
||
.map_err(|_| rustls::Error::General("bad pubkey slice".into()))?;
|
||
|
||
let sig_der = input.signature.signature();
|
||
let sig_raw = sig_from_der(sig_der)
|
||
.map_err(|_| rustls::Error::General("invalid DER signature".into()))?;
|
||
|
||
verify_message(input.message, DEFAULT_ID, pub_key_bytes, &sig_raw)
|
||
.map(|_| HandshakeSignatureValid::assertion())
|
||
.map_err(|_| rustls::Error::General("SM2 signature verification failed".into()))
|
||
}
|
||
|
||
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||
vec![SignatureScheme::SM2_SM3]
|
||
}
|
||
|
||
fn request_ocsp_response(&self) -> bool {
|
||
false
|
||
}
|
||
|
||
fn supported_certificate_types(&self) -> &'static [CertificateType] {
|
||
&[CertificateType::RawPublicKey]
|
||
}
|
||
|
||
fn hash_config(&self, _: &mut dyn core::hash::Hasher) {}
|
||
}
|
||
|
||
// ── 辅助:用 Raw Public Key 模式构建 server/client Config ────────────────────
|
||
|
||
fn make_configs(
|
||
provider: &CryptoProvider,
|
||
server_sec1: Vec<u8>,
|
||
server_spki: Vec<u8>,
|
||
) -> (ServerConfig, ClientConfig) {
|
||
// 服务端:用 new_unchecked 绕过 webpki 对 SM2 OID 的校验
|
||
let server_key_der = pki_types::PrivateKeyDer::Sec1(PrivateSec1KeyDer::from(server_sec1));
|
||
let server_signing_key = provider
|
||
.key_provider
|
||
.load_private_key(server_key_der)
|
||
.unwrap();
|
||
let identity = Arc::new(rustls::crypto::Identity::RawPublicKey(
|
||
SubjectPublicKeyInfoDer::from(server_spki),
|
||
));
|
||
// Reason: Credentials::new_unchecked 跳过 webpki 验证,因为 SM2 OID 尚未被
|
||
// rustls-webpki 支持,但我们的 SignatureVerificationAlgorithm 实现是正确的
|
||
let creds = rustls::crypto::Credentials::new_unchecked(identity, server_signing_key);
|
||
let cert_resolver = Arc::new(rustls::crypto::SingleCredential::from(creds));
|
||
let server_config = ServerConfig::builder(provider.clone().into())
|
||
.with_no_client_auth()
|
||
.with_server_credential_resolver(cert_resolver)
|
||
.unwrap();
|
||
|
||
// 客户端:自定义 verifier 跳过证书链校验
|
||
let verifier = Arc::new(AcceptAllSm2ServerVerifier::new());
|
||
let client_config = ClientConfig::builder(provider.clone().into())
|
||
.dangerous()
|
||
.with_custom_certificate_verifier(verifier)
|
||
.with_no_client_auth()
|
||
.unwrap();
|
||
|
||
(server_config, client_config)
|
||
}
|
||
|
||
// ── 测试用例 ──────────────────────────────────────────────────────────────────
|
||
|
||
/// TLS 1.3 SM4-GCM-SM3 握手自回环(Raw Public Key 模式)
|
||
#[test]
|
||
fn test_tls13_sm4_gcm_sm3_handshake() {
|
||
let provider = rustls_provider::provider();
|
||
let (server_sec1, server_spki) = make_sm2_keypair();
|
||
let (server_config, client_config) = make_configs(&provider, server_sec1, server_spki);
|
||
|
||
let server_name = ServerName::try_from("localhost").unwrap();
|
||
let client_arc = Arc::new(client_config);
|
||
let mut client = client_arc.connect(server_name).build().unwrap();
|
||
let mut server = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||
|
||
do_handshake(&mut client, &mut server);
|
||
|
||
assert_eq!(
|
||
client.negotiated_cipher_suite().unwrap().suite(),
|
||
CipherSuite::TLS13_SM4_GCM_SM3,
|
||
);
|
||
|
||
// server → client 数据传输
|
||
let plaintext = b"Hello, SM2/SM4/SM3!";
|
||
server.writer().write_all(plaintext).unwrap();
|
||
transfer(&mut server, &mut client);
|
||
client.process_new_packets().unwrap();
|
||
let mut received = vec![0u8; plaintext.len()];
|
||
client.reader().read_exact(&mut received).unwrap();
|
||
assert_eq!(received, plaintext.to_vec());
|
||
}
|
||
|
||
/// TLS 1.3 SM4-CCM-SM3 握手自回环
|
||
#[test]
|
||
fn test_tls13_sm4_ccm_sm3_handshake() {
|
||
use std::borrow::Cow;
|
||
|
||
static CCM_SUITES: &[&rustls::Tls13CipherSuite] =
|
||
&[libsmx::rustls_provider::tls13::TLS13_SM4_CCM_SM3];
|
||
|
||
let provider = CryptoProvider {
|
||
tls13_cipher_suites: Cow::Borrowed(CCM_SUITES),
|
||
..rustls_provider::provider()
|
||
};
|
||
|
||
let (server_sec1, server_spki) = make_sm2_keypair();
|
||
let (server_config, client_config) = make_configs(&provider, server_sec1, server_spki);
|
||
|
||
let server_name = ServerName::try_from("localhost").unwrap();
|
||
let client_arc = Arc::new(client_config);
|
||
let mut client = client_arc.connect(server_name).build().unwrap();
|
||
let mut server = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||
|
||
do_handshake(&mut client, &mut server);
|
||
|
||
assert_eq!(
|
||
client.negotiated_cipher_suite().unwrap().suite(),
|
||
CipherSuite::TLS13_SM4_CCM_SM3,
|
||
);
|
||
|
||
// client → server 数据传输
|
||
let msg = b"SM4-CCM test";
|
||
client.writer().write_all(msg).unwrap();
|
||
transfer(&mut client, &mut server);
|
||
server.process_new_packets().unwrap();
|
||
let mut buf = vec![0u8; msg.len()];
|
||
server.reader().read_exact(&mut buf).unwrap();
|
||
assert_eq!(buf, msg.to_vec());
|
||
}
|
||
|
||
/// 验证握手后密钥交换组为 curveSM2
|
||
#[test]
|
||
fn test_kx_group_is_curve_sm2() {
|
||
let provider = rustls_provider::provider();
|
||
let (server_sec1, server_spki) = make_sm2_keypair();
|
||
let (server_config, client_config) = make_configs(&provider, server_sec1, server_spki);
|
||
|
||
let server_name = ServerName::try_from("localhost").unwrap();
|
||
let client_arc = Arc::new(client_config);
|
||
let mut client = client_arc.connect(server_name).build().unwrap();
|
||
let mut server = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||
|
||
do_handshake(&mut client, &mut server);
|
||
|
||
assert_eq!(
|
||
client
|
||
.negotiated_key_exchange_group()
|
||
.map(|g: &dyn rustls::crypto::kx::SupportedKxGroup| g.name()),
|
||
Some(rustls::crypto::kx::NamedGroup::curveSM2),
|
||
);
|
||
}
|
||
|
||
/// 双向数据传输(client ↔ server 各发一条消息)
|
||
#[test]
|
||
fn test_bidirectional_data_transfer() {
|
||
let provider = rustls_provider::provider();
|
||
let (server_sec1, server_spki) = make_sm2_keypair();
|
||
let (server_config, client_config) = make_configs(&provider, server_sec1, server_spki);
|
||
|
||
let server_name = ServerName::try_from("localhost").unwrap();
|
||
let client_arc = Arc::new(client_config);
|
||
let mut client = client_arc.connect(server_name).build().unwrap();
|
||
let mut server = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||
|
||
do_handshake(&mut client, &mut server);
|
||
|
||
// client → server
|
||
let c2s = b"from client";
|
||
client.writer().write_all(c2s).unwrap();
|
||
transfer(&mut client, &mut server);
|
||
server.process_new_packets().unwrap();
|
||
let mut buf = vec![0u8; c2s.len()];
|
||
server.reader().read_exact(&mut buf).unwrap();
|
||
assert_eq!(buf, c2s.to_vec());
|
||
|
||
// server → client
|
||
let s2c = b"from server";
|
||
server.writer().write_all(s2c).unwrap();
|
||
transfer(&mut server, &mut client);
|
||
client.process_new_packets().unwrap();
|
||
let mut buf2 = vec![0u8; s2c.len()];
|
||
client.reader().read_exact(&mut buf2).unwrap();
|
||
assert_eq!(buf2, s2c.to_vec());
|
||
}
|