// 最小 DER 解析与 RSA PKCS#1 v1.5 验签。
//
// 只实现验签需要的那一小部分 DER:SEQUENCE / INTEGER / BIT STRING。
// 模幂复用标准库 BigInt 的 pow(modulus=),填充与 DigestInfo 编码在这里自研。
///|
pub struct RsaPublicKey {
modulus : @bigint.BigInt
exponent : @bigint.BigInt
/// 模数的字节长度,用于定长还原签名整数
byte_len : Int
}
///|
/// 读取一个 DER TLV,返回 (tag, 内容起点, 内容长度, 下一个 TLV 起点)。
fn der_tlv(data : BytesView, offset : Int) -> (Int, Int, Int, Int)? {
let len = data.length()
if offset < 0 || offset + 2 > len {
return None
}
let tag = data[offset].to_int()
let first = data[offset + 1].to_uint()
let content_len = if first < 0x80U {
first.to_int()
} else {
let count = (first & 0x7FU).to_int()
if count == 0 || count > 4 || offset + 2 + count > len {
return None
}
let mut value = 0
for i in 0.. len {
return None
}
Some((tag, content_start, content_len, content_start + content_len))
}
///|
/// 从 RSAPublicKey 的 SEQUENCE 内容起点读取两个 INTEGER。
fn rsa_from_integers(data : BytesView, seq_start : Int) -> RsaPublicKey? {
let n = der_tlv(data, seq_start)
match n {
None => None
Some((tag_n, start_n, len_n, next_n)) => {
if tag_n != 0x02 || len_n == 0 {
return None
}
match der_tlv(data, next_n) {
None => None
Some((tag_e, start_e, len_e, _)) => {
if tag_e != 0x02 || len_e == 0 {
return None
}
let modulus = @bigint.BigInt::from_octets(
data[start_n:start_n + len_n],
)
let exponent = @bigint.BigInt::from_octets(
data[start_e:start_e + len_e],
)
if modulus.is_zero() || exponent.is_zero() {
return None
}
Some({ modulus, exponent, byte_len: (modulus.bit_length() + 7) / 8, })
}
}
}
}
}
///|
/// 解析 DKIM `p=` 里的公钥:优先按 SubjectPublicKeyInfo,失败再按裸 RSAPublicKey。
///
/// 两种形式在真实邮件里都能遇到,因此不能只认一种。
pub fn rsa_public_key_from_der(der : Bytes) -> RsaPublicKey? {
let view : BytesView = der
match der_tlv(view, 0) {
None => None
Some((tag, start, _len, _next)) => {
if tag != 0x30 {
return None
}
match der_tlv(view, start) {
None => None
Some((child_tag, child_start, _child_len, child_next)) => {
// 第一个子节点是 INTEGER:裸 RSAPublicKey
if child_tag == 0x02 {
return rsa_from_integers(view, start)
}
if child_tag != 0x30 {
return None
}
// SPKI:AlgorithmIdentifier 之后必须是 BIT STRING
match der_tlv(view, child_next) {
None => None
Some((bit_tag, bit_start, bit_len, _)) => {
if bit_tag != 0x03 || bit_len < 2 || view[bit_start].to_int() != 0 {
return None
}
match der_tlv(view, bit_start + 1) {
None => None
Some((inner_tag, inner_start, _inner_len, _)) => {
if inner_tag != 0x30 {
return None
}
rsa_from_integers(view, inner_start)
}
}
}
}
}
}
}
}
}
///|
/// SHA-256 的 DigestInfo 前缀(RFC 8017 附录 A.2.4)。
let sha256_digest_info : Array[Byte] = [
b'\x30', b'\x31', b'\x30', b'\x0d', b'\x06', b'\x09', b'\x60', b'\x86', b'\x48',
b'\x01', b'\x65', b'\x03', b'\x04', b'\x02', b'\x01', b'\x05', b'\x00', b'\x04',
b'\x20',
]
///|
/// RSA PKCS#1 v1.5 + SHA-256 验签。
pub fn rsa_verify_sha256(
key : RsaPublicKey,
message : BytesView,
signature : BytesView,
) -> Bool {
let k = key.byte_len
if k < 64 || signature.length() != k {
return false
}
let s = @bigint.BigInt::from_octets(signature)
if s.compare(key.modulus) >= 0 {
return false
}
let m = s.pow(key.exponent, modulus=key.modulus)
let em = m.to_octets(length=k)
if em.length() != k {
return false
}
// EM = 0x00 || 0x01 || PS(0xFF...) || 0x00 || T
if em[0].to_int() != 0x00 || em[1].to_int() != 0x01 {
return false
}
let mut i = 2
while i < k && em[i].to_int() == 0xFF {
i = i + 1
}
if i - 2 < 8 || i >= k || em[i].to_int() != 0x00 {
return false
}
i = i + 1
let digest = sha256(message)
let expected_len = sha256_digest_info.length() + digest.length()
if k - i != expected_len {
return false
}
for j in 0.. Bool {
let der = @base64.decode_lossy(public_key_b64, ignore_whitespace=true)
let signature = @base64.decode_lossy(signature_b64, ignore_whitespace=true)
match rsa_public_key_from_der(der) {
Some(key) => rsa_verify_sha256(key, message, signature)
None => false
}
}