17 个不稳定版本 (3 个重大更改)
0.5.0 | 2024年8月11日 |
---|---|
0.4.3 | 2024年6月19日 |
0.4.2 | 2024年5月29日 |
0.3.10 | 2024年3月28日 |
0.1.0 | 2022年12月1日 |
#8 在 电子邮件
5,433 每月下载量
在 2 crates 中使用
540KB
14K SLoC
mail-auth
mail-auth 是一个用 Rust 编写的电子邮件身份验证和报告库,支持 DKIM、ARC、SPF 和 DMARC 协议。该库旨在快速、安全、正确,同时支持所有主要的 消息身份验证和报告 RFC。
特性
- DomainKeys Identified Mail (DKIM):
- ED25519-SHA256(爱德华曲线数字签名算法)、RSA-SHA256 和 RSA-SHA1 签名和验证。
- DKIM 授权第三方签名。
- 使用滥用报告格式进行 DKIM 失败报告。
- 为 RSA 和 Ed25519(通过
generate
功能启用)生成密钥对。
- Authenticated Received Chain (ARC):
- ED25519-SHA256(爱德华曲线数字签名算法)、RSA-SHA256 和 RSA-SHA1 链验证。
- ARC 封印。
- Sender Policy Framework (SPF):
- 策略评估。
- 使用滥用报告格式进行 SPF 失败报告。
- 基于域的消息身份验证、报告和一致性 (DMARC):
- 策略评估。
- DMARC 聚合报告解析和生成。
- 滥用报告格式 (ARF):
- 滥用和身份验证失败报告。
- 反馈报告解析和生成。
- SMTP TLS 报告:
- 报告解析和生成。
使用示例
DKIM 签名验证
// Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();
// Parse message
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
// Validate signature
let result = resolver.verify_dkim(&authenticated_message).await;
// Make sure all signatures passed verification
assert!(result.iter().all(|s| s.result() == &DkimResult::Pass));
DKIM 签名
// Sign an e-mail message using RSA-SHA256
let pk_rsa = RsaKey::<Sha256>::from_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
let signature_rsa = DkimSigner::from_key(pk_rsa)
.domain("example.com")
.selector("default")
.headers(["From", "To", "Subject"])
.sign(RFC5322_MESSAGE.as_bytes())
.unwrap();
// Sign an e-mail message using ED25519-SHA256
let pk_ed = Ed25519Key::from_bytes(
&base64_decode(ED25519_PUBLIC_KEY.as_bytes()).unwrap(),
&base64_decode(ED25519_PRIVATE_KEY.as_bytes()).unwrap(),
)
.unwrap();
let signature_ed = DkimSigner::from_key(pk_ed)
.domain("example.com")
.selector("default-ed")
.headers(["From", "To", "Subject"])
.sign(RFC5322_MESSAGE.as_bytes())
.unwrap();
// Print the message including both signatures to stdout
println!(
"{}{}{}",
signature_rsa.to_header(),
signature_ed.to_header(),
RFC5322_MESSAGE
);
ARC 链验证
// Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();
// Parse message
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
// Validate ARC chain
let result = resolver.verify_arc(&authenticated_message).await;
// Make sure ARC passed verification
assert_eq!(result.result(), &DkimResult::Pass);
ARC 链封印
// Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();
// Parse message to be sealed
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
// Verify ARC and DKIM signatures
let arc_result = resolver.verify_arc(&authenticated_message).await;
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
// Build Authenticated-Results header
let auth_results = AuthenticationResults::new("mx.mydomain.org")
.with_dkim_result(&dkim_result, "[email protected]")
.with_arc_result(&arc_result, "127.0.0.1".parse().unwrap());
// Seal message
if arc_result.can_be_sealed() {
// Seal the e-mail message using RSA-SHA256
let pk_rsa = RsaKey::<Sha256>::from_pkcs1_pem(RSA_PRIVATE_KEY).unwrap();
let arc_set = ArcSealer::from_key(pk_rsa)
.domain("example.org")
.selector("default")
.headers(["From", "To", "Subject", "DKIM-Signature"])
.seal(&authenticated_message, &auth_results, &arc_result)
.unwrap();
// Print the sealed message to stdout
println!("{}{}", arc_set.to_header(), RFC5322_MESSAGE)
} else {
eprintln!("The message could not be sealed, probably an ARC chain with cv=fail was found.")
}
SPF 策略评估
// Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();
// Verify HELO identity
let result = resolver
.verify_spf_helo("127.0.0.1".parse().unwrap(), "gmail.com", "my-local-domain.org")
.await;
assert_eq!(result.result(), SpfResult::Fail);
// Verify MAIL-FROM identity
let result = resolver
.verify_spf_sender("::1".parse().unwrap(), "gmail.com", "my-local-domain.org", "[email protected]")
.await;
assert_eq!(result.result(), SpfResult::Fail);
DMARC 策略评估
// Create a resolver using Cloudflare DNS
let resolver = Resolver::new_cloudflare_tls().unwrap();
// Verify DKIM signatures
let authenticated_message = AuthenticatedMessage::parse(RFC5322_MESSAGE.as_bytes()).unwrap();
let dkim_result = resolver.verify_dkim(&authenticated_message).await;
// Verify SPF MAIL-FROM identity
let spf_result = resolver
.verify_spf_sender("::1".parse().unwrap(), "example.org", "my-local-domain.org", "[email protected]")
.await;
// Verify DMARC
let dmarc_result = resolver
.verify_dmarc(
&authenticated_message,
&dkim_result,
"example.org",
&spf_result,
|domain| psl::domain_str(domain).unwrap_or(domain),
)
.await;
assert_eq!(dmarc_result.dkim_result(), &DmarcResult::Pass);
assert_eq!(dmarc_result.spf_result(), &DmarcResult::Pass);
更多示例请参阅 examples 目录。
测试与模糊测试
运行测试套件
$ cargo test
使用 cargo-fuzz
对库进行模糊测试
$ cargo +nightly fuzz run mail_auth
符合 RFC
DKIM
- RFC 6376 - DomainKeys Identified Mail (DKIM) 签名
- RFC 6541 - DomainKeys Identified Mail (DKIM) 授权第三方签名
- RFC 6651 - 扩展 DomainKeys Identified Mail (DKIM) 以进行失败报告
- RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA)
- RFC 4686 - 域名标识邮件 (DKIM) 激发威胁的分析
- RFC 5016 - 域名密钥识别邮件(DKIM)签名实践协议需求
- RFC 5585 - 域名密钥识别邮件(DKIM)服务概述
- RFC 5672 - 域名密钥识别邮件(DKIM)签名 -- 更新
- RFC 5863 - 域名密钥识别邮件(DKIM)开发、部署和运营
- RFC 6377 - 域名密钥识别邮件(DKIM)和邮件列表
SPF
DMARC
- RFC 7489 - 基于域的消息认证、报告和一致性(DMARC)
- RFC 8617 - 认证接收链(ARC)协议
- RFC 8601 - 指示消息认证状态的邮件头字段
- RFC 8616 - 国际化邮件的电子邮件认证
- RFC 7960 - 基于域的消息认证、报告和一致性(DMARC)与间接电子邮件流之间的互操作性问题
ARF
- RFC 5965 - 电子邮件反馈报告的可扩展格式
- RFC 6430 - 电子邮件反馈报告类型值:非垃圾邮件
- RFC 6590 - 从邮件滥用报告中删除可能敏感的数据
- RFC 6591 - 使用滥用报告格式进行认证失败报告
- RFC 6650 - 创建和使用电子邮件反馈报告:滥用报告格式(ARF)的应用性声明
SMTP TLS 报告
许可证
根据以下任一许可证授权:
- Apache License, Version 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
根据您的选择。
版权
版权 (C) 2020-2023,Stalwart Labs Ltd.
依赖项
~23–36MB
~665K SLoC