添加 GitHub Pages 文档与 CI 修复
新增功能: - 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
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
//! 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());
|
||||
}
|
||||
Reference in New Issue
Block a user