增加RC数据结构和资源集合数据结构 (#1)

Co-authored-by: xiuting.xu <xiutingxt.xu@gmail.com>
Reviewed-on: #1
Reviewed-by: yuyr <yuyr@zgclab.edu.cn>
Co-authored-by: xuxt <xuxt@zgclab.edu.cn>
Co-committed-by: xuxt <xuxt@zgclab.edu.cn>
This commit is contained in:
xuxt 2026-02-02 15:37:05 +08:00 committed by yuyr
parent 5e474fffd2
commit 421847d329
13 changed files with 1203 additions and 1 deletions

View File

@ -9,3 +9,4 @@ hex = "0.4.3"
thiserror = "2.0.18" thiserror = "2.0.18"
time = "0.3.45" time = "0.3.45"
x509-parser = { version = "0.18.0", features = ["verify"] } x509-parser = { version = "0.18.0", features = ["verify"] }
url = "2.5.8"

36
specs/01_tal.md Normal file
View File

@ -0,0 +1,36 @@
# 01. Trust Anchor Locator (TAL)
## 1.1 对象定位
TAL是一个数据格式/配置文件目的是告诉RP信任锚的公钥是什么以及相关对象可以从哪里获取。
## 1.2 数据格式 RFC 8630 §2.2
TAL是一个配置文件格式定义如下
```
The TAL is an ordered sequence of:
1. an optional comment section consisting of one or more lines each starting with the "#" character, followed by human-readable informational UTF-8 text, conforming to the restrictions defined
in Section 2 of [RFC5198], and ending with a line break,
2. a URI section that is comprised of one or more ordered lines, each containing a TA URI, and ending with a line break,
3. a line break, and
4. a subjectPublicKeyInfo [RFC5280] in DER format [X.509], encoded in base64 (see Section 4 of [RFC4648]). To avoid long lines,
line breaks MAY be inserted into the base64-encoded string.
Note that line breaks in this file can use either "<CRLF>" or "<LF>".
```
## 1.3 抽象数据模型
### 1.3.1 TAL
| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |
|----------|-------------|-------------------------|--------------------------------------------|---------------|
| uris | Vec<TalUri> | 指向TA的URI列表 | 允许rsync和https协议。 | RFC 8630 §2.1 |
| comment | Vec<String> | 注释(可选) | | RFC 8630 §2.2 |
| spki_der | Vec<u8> | 原始的subjectPublicKeyInfo | x.509 SubjectPublicKeyInfo DER编码再base64编码 | RFC 8630 §2.2 |
### 1.3.2 TalUri
| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |
|-------|--------|---------|---------|---------------|
| Rsync | String | rsync地址 | | RFC 8630 §2.1 |
| Https | String | https地址 | | RFC 8630 §2.1 |

121
specs/02_ta.md Normal file
View File

@ -0,0 +1,121 @@
# 02. Trust Anchor (TA)
## 2.1 对象定位
TA是一个自签名的CA证书。
## 2.2 原始载体与编码
- 载体X.509 certificates.
- 编码DER遵循 RFC 5280 的 certificate 结构与字段语义但受限于RFC 8630 §2.3
## 2.3 抽象数据类型
### 2.3.1 TA
| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |
|-------------------|------------------|---------------|---------|---------------|
| name | String | 标识该TA如apnic等 | | |
| cert_der | Vec<u8> | 原始DER内容 | | |
| cert | X509Certificate | 基础X509证书 | | RFC 5280 §4.1 |
| resource | ResourceSet | 资源集合 | | |
| publication_point | Uri | 获取该TA的URI | | |
### 2.3.2 ResourceSet
资源集合是来自RFC 3779的IP地址块§2和AS号段§3)受约束于RFC 8630 §2.3
| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |
|------|----------------|--------|-------------|---------------------------|
| ips | IpResourceSet | IP地址集合 | 不能是inherit | RFC 3779 §2和RFC 8630 §2.3 |
| asns | AsnResourceSet | ASN集合 | 不能是inherit | RFC 3779 §3和RFC 8630 §2.3 |
[//]: # ()
[//]: # (### 2.3.3 IpResourceSet)
[//]: # (包括IPv4和IPv6的前缀表示)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|----|------------------------|----------|-------------|--------------|)
[//]: # (| v4 | PrefixSet<Ipv4Prefix> | IPv4前缀集合 | | RFC 3779 §2 |)
[//]: # (| v6 | PrefixSet<Ipv6Prefix> | IPv6前缀集合 | | RFC 3779 §2 |)
[//]: # ()
[//]: # (### 2.3.4 AsnResourceSet)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|-------|--------------------|-------|-------------|-------------|)
[//]: # (| range | RangeSet<AsnBlock> | ASN集合 | | RFC 3779 §3 |)
[//]: # ()
[//]: # (### 2.3.5 Ipv4Prefix)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|------|-----|-----|---------|-------------|)
[//]: # (| addr | u32 | 地址 | | RFC 3779 §2 |)
[//]: # (| len | u8 | 长度 | 0-32 | RFC 3779 §2 |)
[//]: # ()
[//]: # ()
[//]: # (### 2.3.6 Ipv6Prefix)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|------|------|-----|---------|-------------|)
[//]: # (| addr | u128 | 地址 | | RFC 3779 §2 |)
[//]: # (| len | u8 | 长度 | 0-128 | RFC 3779 §2 |)
[//]: # ()
[//]: # (### 2.3.7 AsnBlock)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|----------|----------|-------|---------|--------------|)
[//]: # (| asn | Asn | ASN | | RFC 3779 §3 |)
[//]: # (| asnRange | AsnRange | ASN范围 | | RFC 3779 §3 |)
[//]: # ()
[//]: # ()
[//]: # (### 2.3.8 Asn)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|-----|-----|-----|---------|-------------|)
[//]: # (| asn | u32 | ASN | | RFC 3779 §3 |)
[//]: # ()
[//]: # (### 2.3.8 AsnRange)
[//]: # ()
[//]: # (| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |)
[//]: # (|-----|-----|-------|---------|--------------|)
[//]: # (| min | Asn | 最小ASN | | RFC 3779 §3 |)
[//]: # (| max | Asn | 最大ASN | | RFC 3779 §3 |)
# 2.4 TA校验流程RFC 8630 §3
1. 从TAL的URI列表中获取证书对象。顺序访问若前面失效再访问后面的
2. 验证证书格式必须是当前、有效的自签名RPKI证书。
3. 验证公钥匹配。TAL中的SubjectPublicKeyInfo与下载证书的公钥一致。
4. 其他检查。
5. 更新本地存储库缓存。

314
specs/03_rc.md Normal file
View File

@ -0,0 +1,314 @@
# 03. RC (Resource Certifications)
## 3.1 对象定位
RC是资源证书包括CA和EE
## 3.2 原始载体与编码
- 载体X.509 certificates.
- 编码DER遵循 RFC 5280 的 Certificate 结构与字段语义,但受 RPKI profile 限制RFC 6487 §4
### 3.2.1 基本语法RFC 5280 §4RFC 6487
RC是遵循RFC5280定义的X.509Certificate语法(RFC 5280 §4)并且符合RFC 6487 §4的约束。只选取RFC 6487 §4章节列出来的字段。Unless specifically noted as being OPTIONAL, all the fields listed
here MUST be present, and any other fields MUST NOT appear in a
conforming resource certificate.
```
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version MUST be v3,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
subject Name,
validity Validity,
subjectPublicKeyInfo SubjectPublicKeyInfo,
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version MUST be v3
}
Version ::= INTEGER { v1(0), v2(1), v3(2) }
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time }
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
-- contains the DER encoding of an ASN.1 value
-- corresponding to the extension type identified
-- by extnID
}
```
> 其中`Name` "a valid X.501 distinguished name"(RFC 6487 §4.4)
### 3.2.2 证书扩展字段 RFC 6487 §4.8)
RC的证书扩展字段按照RFC 6487 §4.8的规定,有以下几个扩展:
- Basic Constraints
- Subject Key Identifier
- Authority Key Identifier
- Key Usage
- Extended Key Usage(CA证书以及验证RPKI对象的EE证书不能出现该字段。非RPKI对象的EE可以出现EKU但必须为non-critical)
- CRL Distribution Points
- Authority Information Access
- Subject Information Access
- SIA for CA Certificates
- SIA for EE Certificates
- Certificate Policies
- IP Resources
- AS Resources
```
# Basic Constraints
id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
BasicConstraints ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE }
# Subject Key Identifier
id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
SubjectKeyIdentifier ::= KeyIdentifier
KeyIdentifier ::= OCTET STRING
# Authority Key Identifier
id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL }
# Key Usage
id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
KeyUsage ::= BIT STRING {
digitalSignature (0),
nonRepudiation (1), -- recent editions of X.509 have
-- renamed this bit to contentCommitment
keyEncipherment (2),
dataEncipherment (3),
keyAgreement (4),
keyCertSign (5),
cRLSign (6),
encipherOnly (7),
decipherOnly (8) }
# Extended Key Usage
id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
KeyPurposeId ::= OBJECT IDENTIFIER
# CRL Distribution Points
id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
DistributionPoint ::= SEQUENCE {
distributionPoint [0] DistributionPointName OPTIONAL }
DistributionPointName ::= CHOICE {
fullName [0] GeneralNames }
## Authority Information Access
id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
AuthorityInfoAccessSyntax ::=
SEQUENCE SIZE (1..MAX) OF AccessDescription
AccessDescription ::= SEQUENCE {
accessMethod OBJECT IDENTIFIER,
accessLocation GeneralName }
# AccessDescription
id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
# CA 证书发布位置
id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
# OCSP 服务地址
id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
# Subject Information Access
id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 }
SubjectInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription
AccessDescription ::= SEQUENCE {
accessMethod OBJECT IDENTIFIER,
accessLocation GeneralName }
## Subject Information Access for CA (RFC 6487 §4.8.8.1)
id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
id-ad-rpkiManifest OBJECT IDENTIFIER ::= { id-ad 10 }
必须存在一个accessMethod=id-ad-caRepositoryaccessLocation=rsyncURI。
必须存在一个accessMethod=id-ad-repiManifest, accessLocation=rsync URI指向该CA的mft对象。
## Subject Information Access for EE (RFC 6487 §4.8.8.2)
id-ad-signedObject OBJECT IDENTIFIER ::= { id-ad 11 }
必须存在一个accessMethod=id-ad-signedObject, accessLocation=rsyncURI
不允许其他的accessMethod
# Certificate Policies
id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
PolicyInformation ::= SEQUENCE {
policyIdentifier CertPolicyId,
policyQualifiers SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL }
CertPolicyId ::= OBJECT IDENTIFIER
PolicyQualifierInfo ::= SEQUENCE {
policyQualifierId PolicyQualifierId,
qualifier ANY DEFINED BY policyQualifierId }
-- policyQualifierIds for Internet policy qualifiers
id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
Qualifier ::= CHOICE {
cPSuri CPSuri,
userNotice UserNotice }
CPSuri ::= IA5String
UserNotice ::= SEQUENCE {
noticeRef NoticeReference OPTIONAL,
explicitText DisplayText OPTIONAL }
NoticeReference ::= SEQUENCE {
organization DisplayText,
noticeNumbers SEQUENCE OF INTEGER }
DisplayText ::= CHOICE {
ia5String IA5String (SIZE (1..200)),
visibleString VisibleString (SIZE (1..200)),
bmpString BMPString (SIZE (1..200)),
utf8String UTF8String (SIZE (1..200)) }
# IP Resources
id-pe-ipAddrBlocks OBJECT IDENTIFIER ::= { id-pe 7 }
IPAddrBlocks ::= SEQUENCE OF IPAddressFamily
IPAddressFamily ::= SEQUENCE { -- AFI & optional SAFI --
addressFamily OCTET STRING (SIZE (2..3)),
ipAddressChoice IPAddressChoice }
IPAddressChoice ::= CHOICE {
inherit NULL, -- inherit from issuer --
addressesOrRanges SEQUENCE OF IPAddressOrRange }
IPAddressOrRange ::= CHOICE {
addressPrefix IPAddress,
addressRange IPAddressRange }
IPAddressRange ::= SEQUENCE {
min IPAddress,
max IPAddress }
IPAddress ::= BIT STRING
# AS Resources
id-pe-autonomousSysIds OBJECT IDENTIFIER ::= { id-pe 8 }
ASIdentifiers ::= SEQUENCE {
asnum [0] EXPLICIT ASIdentifierChoice OPTIONAL,
rdi [1] EXPLICIT ASIdentifierChoice OPTIONAL}
ASIdentifierChoice ::= CHOICE {
inherit NULL, -- inherit from issuer --
asIdsOrRanges SEQUENCE OF ASIdOrRange }
ASIdOrRange ::= CHOICE {
id ASId,
range ASRange }
ASRange ::= SEQUENCE {
min ASId,
max ASId }
ASId ::= INTEGER
```
# 3.3 抽象数据结构
采用X509 Certificate + Resource + 约束校验的方式组合
| 字段 | 类型 | 语义 | 约束/解析规则 | RFC 引用 |
|----------|---------------------|----------|---------|---------------|
| cert_der | Vec<u8> | 证书原始数据 | | |
| cert | X509Certificate | 基础X509证书 | | RFC 5280 §4.1 |
| resource | ResourceSet | 资源集合 | | |
# 3.4 约束规则
## 3.4.1 Cert约束校验规则
RFC 6487中规定的证书的字段参见[3.2.1 ](#321-基本语法rfc-5280-4rfc-6487-)
-
| 字段 | 语义 | 约束/解析规则 | RFC 引用 |
|-----------|-------|----------------------------------------------|--------------|
| version | 证书版本 | 必须是v3(值为2 | RFC6487 §4.1 |
| serial | 证书编号 | 同一个CA签发的证书编号必须唯一 | RFC6487 §4.2 |
| validity | 证书有效期 | notBefore时间不能早于证书的生成时间。若时间段大于上级证书的有效期也是有效的 | RFC6487 §4.6 |
## 3.4.2 Cert Extentions中字段的约束校验规则
RFC 6487中规定的扩展字段参见[3.2.2 ](#322-证书扩展字段-rfc-6487-48)
| 字段 | critical | 语义 | 约束/解析规则 | RFC 引用 |
|----------------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
| basicConstraints | Y | 证书类型 | CA证书cA=TRUE; EE证书cA=FALSE | RFC6487 §4.8.1 |
| subjectKeyIdentifier | N | 证书公钥 | SKI = SHA-1(DER-encoded SPKI bit string) | RFC6487 §4.8.2 |
| authorityKeyIdentifier | N | 父证书的公钥 | 字段只包含keyIdentifier不能包含authorityCertIssuer和authorityCertSerialNumber除了自签名CA外其余证书必须出现。自签名CA若出现该字段则等于SKI | RFC6487 §4.8.3 |
| keyUsage | Y | 证书公钥的用途权限 | CA证书keyCertSign = TRUE, cRLSign = TRUE 其他都是FALSE。EE证书digitalSignature = TRUE 其他都是FALSE | RFC6487 §4.8.4 |
| extendedKeyUsage | N | 扩展证书公钥的用途权限 | CA证书不能出现EKU验证 RPKI 对象的 EE 证书不能出现EKU非 RPKI 对象的 EE可以出现EKU但必须为non-critical. | RFC6487 §4.8.5 |
| cRLDistributionPoints | N | CRL的发布点位置 | 字段distributionPoint不能包含reasons、cRLIssuer。其中distributionPoint字段包含fullName不能包含nameRelativeToCRLIssuer。fullName的格式必须是URI。自签名证书禁止出现该字段。非自签名证书必须出现。一个CA只能有一个CRL。一个CRLDP只能包含一个distributionPoint。但一个distributionPoint字段中可以包含多于1个的URI但必须包含rsync URI且必须是最新的。 | RFC6487 §4.8.6 |
| authorityInformationAccess | N | 签发者的发布点位置 | 除了自签名的CA必须出现。自签名CA禁止出现。推荐的URI访问方式是rsync并且rsyncURI的话必须指定accessMethod=id-ad-caIssuers | RFC6487 §4.8.7 |
| subjectInformationAccess | N | 发布点位置 | CA证书必须存在。必须存在一个accessMethod=id-ad-caRepositoryaccessLocation=rsyncURI。必须存在一个accessMethod=id-ad-repiManifest,accessLocation=rsync URI指向该CA的mft对象。 EE证书必须存在。必须存在一个accessMethod=id-ad-signedObject,accessLocation=rsyncURI。不允许其他的accessMethod | RFC6487 §4.8.8 |
| certificatePolicies | Y | 证书策略 | 必须存在并且只能存在一种策略RFC 6484 — RPKI Certificate Policy (CP) | RFC6487 §4.8.9 |
| iPResources | Y | IP地址集合 | 所有的RPKI证书中必须包含IP Resources或者ASResources或者两者都包含。 | RFC6487 §4.8.10 |
| aSResources | Y | ASN集合 | 所有的RPKI证书中必须包含IP Resources或者ASResources或者两者都包含。 | RFC6487 §4.8.11 |

View File

@ -1,2 +1,6 @@
pub mod crl; pub mod crl;
mod rc;
mod tal;
mod ta;
mod resources;
mod oids;

13
src/data_model/oids.rs Normal file
View File

@ -0,0 +1,13 @@
pub const OID_BASIC_CONSTRAINTS: &str = "2.5.29.19";
pub const OID_SUBJECT_KEY_IDENTIFIER: &str = "2.5.29.14";
pub const OID_AUTHORITY_KEY_IDENTIFIER: &str = "2.5.29.35";
pub const OID_KEY_USAGE: &str = "2.5.29.15";
pub const OID_EXTENDED_KEY_USAGE: &str = "2.5.29.37";
pub const OID_CRL_DISTRIBUTION_POINTS: &str = "2.5.29.31";
pub const OID_AUTHORITY_INFO_ACCESS: &str = "1.3.6.1.5.5.7.1.1";
pub const OID_ACCESS_DESCRIPTION: &str = "1.3.6.1.5.5.7.48";
pub const OID_AD_CA_ISSUERS: &str = "1.3.6.1.5.5.7.48.2";
pub const OID_AD_OCSP: &str = "1.3.6.1.5.5.7.48.1";
pub const OID_SUBJECT_INFO_ACCESS: &str = "1.3.6.1.5.5.7.1.11";
pub const OID_CERTIFICATE_POLICIES: &str = "2.5.29.32";
pub const OID_SHA256_WITH_RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.11";

519
src/data_model/rc.rs Normal file
View File

@ -0,0 +1,519 @@
use der_parser::asn1_rs::Tag;
use der_parser::num_bigint::BigUint;
use url::Url;
use time::OffsetDateTime;
use x509_parser::x509::AlgorithmIdentifier;
use x509_parser::prelude::{Validity, KeyUsage, X509Certificate, FromDer,
X509Version, X509Extension, ParsedExtension,
CRLDistributionPoints, DistributionPointName, GeneralName};
use crate::data_model::crl::CrlDecodeError;
use crate::data_model::resources::ip_resources::IPAddrBlocks;
use crate::data_model::resources::as_resources::ASIdentifiers;
use crate::data_model::oids;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SubjectPublicKeyInfo {
pub algorithm_oid: String,
pub subject_public_key: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AccessDescription {
pub access_method_oid: String,
pub access_location: Url,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PolicyInformation {
pub policy_oid: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RcExtension {
pub basic_constraints: bool,
pub subject_key_identifier: u8,
pub authority_key_identifier: u8,
pub key_usage: KeyUsage,
pub extended_key_usage_oid: u8,
pub crl_distribution_points: Vec<Url>,
pub authority_info_access: Vec<AccessDescription>,
pub subject_info_access: Vec<AccessDescription>,
pub certificate_policies: Vec<PolicyInformation>,
pub ip_resource: IPAddrBlocks,
pub as_resource: ASIdentifiers,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResourceCert {
/// 证书原始DER内容
pub cert_der: Vec<u8>,
/// 基本证书信息
pub version: u32,
pub serial_number: BigUint,
pub signature_algorithm_oid: String,
pub issuer_dn: String,
pub subject_dn: String,
pub validity: Validity,
pub subject_public_key_info: SubjectPublicKeyInfo,
pub extensions: RcExtension,
}
#[derive(Debug, thiserror::Error)]
pub enum ResourceCertError {
#[error("X.509 parse resource cert error: {0}")]
ParseCert(String),
#[error("trailing bytes after CRL DER: {0} bytes")]
TrailingBytes(usize),
#[error("invalid version {0}")]
InvalidVersion(u32),
#[error("signatureAlgorithm does not match tbsCertificate.signature")]
SignatureAlgorithmMismatch,
#[error("unsupported signature algorithm")]
UnsupportedSignatureAlgorithm,
#[error("invalid Cert signature algorithm parameters")]
InvalidSignatureParameters,
#[error("invalid Cert validity range")]
InvalidValidityRange,
#[error("Cert not yet valid")]
NotYetValid,
#[error("expired")]
Expired,
#[error("Critical error, {0} should be {1}")]
CriticalError(String, String),
#[error("Duplicate Extension: {0}")]
DuplicateExtension(String),
#[error("AKI missing keyIdentifier")]
AkiMissingKeyIdentifier,
#[error("Unexpected parameter: {0}")]
UnexceptedParameter(String),
#[error("Missing parameter: {0}")]
MissingParameter(String),
#[error("CRL DP invalid distributionPointName: {0}")]
CrlDpInvalidDistributionPointName(String),
#[error("CRL DP unexpected distributionPointType: {0}")]
CrlDpUnexpectedDistributionPointType(String),
#[error("invalid URI: {0}")]
InvalidUri(String),
#[error("Unsupported General Name in {0}")]
UnsupportedGeneralName(String),
#[error("Unsupported CRL Distribution Point")]
UnsupportedCrlDistributionPoint,
#[error("Invalid Access Location Type")]
InvalidAccessLocationType,
#[error("Empty AuthorityInfoAccess!")]
EmptyAuthorityInfoAccess,
}
// impl ResourceCert{
// pub fn from_der(cert_der: &[u8]) -> Result<Self, ResourceCertError> {
// let (rem, x509_rc) = X509Certificate::from_der(cert_der)
// .map_err(|e| ResourceCertError::ParseCert(e.to_string()))?;
//
// if !rem.is_empty() {
// return Err(ResourceCertError::TrailingBytes(rem.len()));
// }
//
// // 校验
// parse_and_validate_cert(x509_rc)
// }
//
//
//
// }
//
// fn parse_and_validate_cert(x509_rc: X509Certificate) -> Result<ResourceCert, ResourceCertError> {
// ///逐个校验RC的内容, 如果有任何一个校验失败, 则返回错误
//
// // 1. 版本号必须是V3
// let version = match x509_rc.version() {
// X509Version::V3 => X509Version::V3,
// v => {
// return Err(ResourceCertError::InvalidVersion(v.0));
// }
// };
//
// // 2.校验签名算法
// // 2.1. 校验外层的签名算法与里层的一致
// let outer = &x509_rc.signature_algorithm;
// let inner = &x509_rc.tbs_certificate.signature;
//
// if outer.algorithm != inner.algorithm || outer.parameters != inner.parameters {
// return Err(ResourceCertError::SignatureAlgorithmMismatch);
// }
// //2.2 RPKI的签名算法必须是rsaWithSHA256
// let signature_algorithm = &x509_rc.signature_algorithm;
// if signature_algorithm.algorithm.to_id_string() != oids::OID_SHA256_WITH_RSA_ENCRYPTION {
// return Err(ResourceCertError::UnsupportedSignatureAlgorithm);
// }
// validate_sig_params(signature_algorithm)?;
//
// // 3. 校验Validity
// let validity = x509_rc.validity();
// validate_validity(validity, OffsetDateTime::now_utc())?;
//
// // 4. SubjectPublicKeyInfo
// let subject_public_key_info = x509_rc.tbs_certificate.subject_pki;
//
// let extensions = parse_and_validate_extensions(x509_rc.extensions())?;
//
// Ok(ResourceCert {
// cert_der: x509_rc.to_der().to_vec(),
// version: version.0,
// serial_number: x509_rc.serial(),
// signature_algorithm_oid: signature_algorithm.algorithm.to_id_string(),
// issuer_dn: x509_rc.issuer().to_string(),
// subject_dn: x509_rc.subject().to_string(),
// validity,
// subject_public_key_info: SubjectPublicKeyInfo {
// // algorithm_oid: x509_rc.tbs_certificate.subject_pki.algorithm.algorithm.to_id_string(),
// // subject_public_key: x509_rc.tbs_certificate.subject_pki.subject_public_key.unused_bits,
// },
// extensions,
// })
//
//
// }
//
// fn validate_sig_params(sig: &AlgorithmIdentifier<'_>) -> Result<(), CrlDecodeError> {
// match sig.parameters.as_ref() {
// None => Ok(()),
// Some(p) if p.tag() == Tag::Null => Ok(()),
// Some(_p) => Err(CrlDecodeError::InvalidSignatureAlgorithmParameters),
// }
// }
//
// fn validate_validity(
// validity: &Validity,
// now: OffsetDateTime,
// ) -> Result<(), ResourceCertError> {
// let not_before = validity.not_before.to_datetime();
// let not_after = validity.not_after.to_datetime();
//
// if not_after < not_before {
// return Err(ResourceCertError::InvalidValidityRange);
// }
//
// if now < not_before {
// return Err(ResourceCertError::NotYetValid);
// }
//
// if now > not_after {
// return Err(ResourceCertError::Expired);
// }
//
// Ok(())
// }
//
//
// pub fn parse_and_validate_extensions(
// exts: &[X509Extension<'_>],
// ) -> Result<RcExtension, ResourceCertError> {
// let mut basic_constraints = None;
// let mut ip_addr_blocks = None;
// let mut as_identifiers = None;
// let mut ski = None;
// let mut aki = None;
// let mut crl_dp = None;
// let mut aia = None;
// let mut sia = None;
// let mut key_usage = None;
// let mut extended_key_usage = None;
// let mut certificate_policies = None;
//
// for ext in exts {
// let oid = ext.oid.to_id_string();
// let critical = ext.critical;
// match oid.as_str() {
// oids::OID_BASIC_CONSTRAINTS => {
// if basic_constraints.is_some() {
// return Err(ResourceCertError::DuplicateExtension("basicConstraints".into()));
// }
// if !critical {
// return Err(ResourceCertError::CriticalError("basicConstraints".into(), "critical".into()));
// }
// let bc = parse_basic_constraints(ext)?;
// basic_constraints = Some(bc);
// }
// oids::OID_SUBJECT_KEY_IDENTIFIER => {
// if ski.is_some() {
// return Err(ResourceCertError::DuplicateExtension("subjectKeyIdentifier".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("subjectKeyIdentifier".into(), "non-critical".into()));
// }
// let s = parse_subject_key_identifier(ext)?;
// ski = Some(s);
// }
// oids::OID_AUTHORITY_KEY_IDENTIFIER => {
// if aki.is_some() {
// return Err(ResourceCertError::DuplicateExtension("authorityKeyIdentifier".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("authorityKeyIdentifier".into(), "non-critical".into()));
// }
// let a = parse_authority_key_identifier(ext)?;
// aki = Some(a);
// }
// oids::OID_KEY_USAGE => {
// if key_usage.is_some() {
// return Err(ResourceCertError::DuplicateExtension("keyUsage".into()));
// }
// if !critical {
// return Err(ResourceCertError::CriticalError("keyUsage".into(), "critical".into()));
// }
// let ku = parse_key_usage(ext)?;
// key_usage = Some(ku);
// }
// oids::OID_EXTENDED_KEY_USAGE => {
// if extended_key_usage.is_some() {
// return Err(ResourceCertError::DuplicateExtension("extendedKeyUsage".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("extendedKeyUsage".into(), "non-critical".into()));
// }
// let eku = oids::OID_EXTENDED_KEY_USAGE;
// }
// oids::OID_CRL_DISTRIBUTION_POINTS => {
// if crl_dp.is_some() {
// return Err(ResourceCertError::DuplicateExtension("crlDistributionPoints".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("crlDistributionPoints".into(), "non-critical".into()));
// }
// let cdp = parse_crl_distribution_points(ext)?;
// crl_dp = Some(cdp);
// }
// oids::OID_AUTHORITY_INFO_ACCESS => {
// if aia.is_some() {
// return Err(ResourceCertError::DuplicateExtension("authorityInfoAccess".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("authorityInfoAccess".into(), "non-critical".into()));
// }
// let p_aia = parse_authority_info_access(ext)?;
// aia = Some(p_aia);
// }
// oids::OID_SUBJECT_INFO_ACCESS => {
// if sia.is_some() {
// return Err(ResourceCertError::DuplicateExtension("subjectInfoAccess".into()));
// }
// if critical {
// return Err(ResourceCertError::CriticalError("subjectInfoAccess".into(), "non-critical".into()));
// }
// let p_sia = parse_subject_info_access(ext)?;
// sia = Some(p_sia);
// }
// oids::OID_CERTIFICATE_POLICIES => {
// if certificate_policies.is_some() {
// return Err(ResourceCertError::DuplicateExtension("certificatePolicies".into()));
// }
// if !critical {
// return Err(ResourceCertError::CriticalError("certificatePolicies".into(), "critical".into()));
// }
// let p_cp = parse_certificate_policies(ext)?;
// certificate_policies = Some(p_cp);
// }
// }
//
//
// }
// Ok(RcExtension {
// basic_constraints,
// ip_addr_blocks,
// as_identifiers,
// subject_key_id: ski,
// authority_key_id: aki,
// crl_distribution_points: crl_dp,
// authority_info_access: aia,
// })
// }
//
// fn parse_basic_constraints(ext: &X509Extension<'_>) -> Result<bool, ResourceCertError> {
// let ParsedExtension::BasicConstraints(bc) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert("basicConstraints parse failed".into()));
// };
// Ok(bc.ca)
// }
//
// fn parse_subject_key_identifier(ext: &X509Extension<'_>) -> Result<Vec<u8>, ResourceCertError> {
// let ParsedExtension::SubjectKeyIdentifier(s) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert("subjectKeyIdentifier parse failed".into()));
// };
// Ok(s.0.to_vec())
// }
//
// fn parse_authority_key_identifier(ext: &X509Extension<'_>) -> Result<Vec<u8>, ResourceCertError> {
// let ParsedExtension::AuthorityKeyIdentifier(aki) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert("authorityKeyIdentifier parse failed".into()));
// };
// let key_id = aki
// .key_identifier
// .as_ref()
// .ok_or(ResourceCertError::MissingParameter("key_identifier".into()))?;
//
// if aki.authority_cert_issuer.is_some() {
// return Err(ResourceCertError::UnexceptedParameter("authority_cert_issuer".into()));
// }
// if aki.authority_cert_serial.is_some() {
// return Err(ResourceCertError::UnexceptedParameter("authority_cert_serial".into()));
// }
//
//
// Ok(key_id.0.to_vec())
// }
//
// fn parse_key_usage(ext: &X509Extension<'_>) -> Result<KeyUsage, ResourceCertError> {
// let ParsedExtension::KeyUsage(ku) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert("keyUsage parse failed".into()));
// };
// Ok(ku.clone())
// }
//
// fn parse_crl_distribution_points(ext: &X509Extension<'_>) -> Result<Vec<Url>, ResourceCertError> {
// let ParsedExtension::CRLDistributionPoints(cdp) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert("crlDistributionPoints parse failed".into()));
// };
// let mut urls = Vec::new();
// for point in cdp.points.iter() {
// if point.reasons.is_some() {
// return Err(ResourceCertError::UnexceptedParameter("reasons".into()));
// }
// if point.crl_issuer.is_some() {
// return Err(ResourceCertError::UnexceptedParameter("crl_issuer".into()));
// }
//
// let dp_name = point.distribution_point.as_ref()
// .ok_or(ResourceCertError::MissingParameter("distribution_point".into()))?;
// match dp_name {
// DistributionPointName::FullName(names) => {
// for name in names {
// match name {
// GeneralName::URI(uri) => {
// let url = Url::parse(uri)
// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?;
// urls.push(url);
// }
// _ => {
// return Err(ResourceCertError::UnsupportedGeneralName("distribution_point".into()));
// }
// }
// }
//
// }
// DistributionPointName::NameRelativeToCRLIssuer(_) => {
// return Err(ResourceCertError::UnsupportedCrlDistributionPoint);
// }
// }
// }
// if urls.is_empty() {
// return Err(ResourceCertError::MissingParameter("distribution_point".into()));
// }
// Ok(urls)
// }
//
// fn parse_authority_info_access(
// ext: &X509Extension<'_>,
// ) -> Result<Vec<AccessDescription>, ResourceCertError> {
// let ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert(
// "authorityInfoAccess parse failed".into(),
// ));
// };
//
// let mut access_descriptions = Vec::new();
//
// for access in &aia.accessdescs {
// let access_method_oid = access.access_method.to_id_string();
//
// let uri = match &access.access_location {
// GeneralName::URI(uri) => uri,
// _ => {
// return Err(ResourceCertError::InvalidAccessLocationType);
// }
// };
//
// let url = Url::parse(uri)
// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?;
//
// access_descriptions.push(AccessDescription {
// access_method_oid,
// access_location: url,
// });
// }
//
// if access_descriptions.is_empty() {
// return Err(ResourceCertError::EmptyAuthorityInfoAccess);
// }
//
// Ok(access_descriptions)
// }
//
// fn parse_subject_info_access(ext: &X509Extension<'_>) -> Result<Vec<AccessDescription>, ResourceCertError> {
// let ParsedExtension::SubjectInfoAccess(sia) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert(
// "subjectInfoAccess parse failed".into(),
// ));
// };
// let mut access_descriptions = Vec::new();
//
// for access in &sia.accessdescs {
// let access_method_oid = access.access_method.to_id_string();
//
// // accessLocation: MUST be URI in RPKI
// let uri = match &access.access_location {
// GeneralName::URI(uri) => uri,
// _ => {
// return Err(ResourceCertError::InvalidAccessLocationType);
// }
// };
//
// let url = Url::parse(uri)
// .map_err(|_| ResourceCertError::InvalidUri(uri.to_string()))?;
//
// access_descriptions.push(AccessDescription {
// access_method_oid,
// access_location: url,
// });
// }
//
// if access_descriptions.is_empty() {
// return Err(ResourceCertError::EmptyAuthorityInfoAccess);
// }
//
// Ok(access_descriptions)
// }
//
// fn parse_certificate_policies(ext: &X509Extension<'_>) -> Result<Vec<PolicyInformation>, ResourceCertError> {
// let ParsedExtension::CertificatePolicies(cp) = ext.parsed_extension() else {
// return Err(ResourceCertError::ParseCert(
// "certificatePolicies parse failed".into(),
// ));
// };
// let mut policies = Vec::new();
//
// }

View File

@ -0,0 +1,90 @@
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ASIdentifiers {
pub asn: Vec<ASIdentifierChoice>
}
// ASN
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ASIdentifierChoice {
Inherit,
ASIDsOrRanges(Vec<ASIDOrRange>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ASIDOrRange {
Id(Asn),
AsRange(ASRange),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ASRange {
pub min: Asn,
pub max: Asn,
}
impl ASRange {
/// Creates a new AS number range from the smallest and largest number.
pub fn new(min: Asn, max: Asn) -> Self {
ASRange { min, max }
}
/// Returns an AS block covering all ASNs.
pub fn all() -> ASRange {
ASRange::new(Asn::MIN, Asn::MAX)
}
/// Returns the smallest AS number that is part of this range.
pub fn min(self) -> Asn {
self.min
}
/// Returns the largest AS number that is still part of this range.
pub fn max(self) -> Asn {
self.max
}
/// Returns the number of ASNs covered by this value.
pub fn asn_count(self) -> u32 {
u32::from(self.max) - u32::from(self.min) + 1
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Asn(u32);
impl Asn {
pub const MIN: Asn = Asn(u32::MIN);
pub const MAX: Asn = Asn(u32::MAX);
/// Creates an AS number from a `u32`.
pub fn from_u32(value: u32) -> Self {
Asn(value)
}
/// Converts an AS number into a `u32`.
pub fn into_u32(self) -> u32 {
self.0
}
/// Converts an AS number into a network-order byte array.
pub fn to_raw(self) -> [u8; 4] {
self.0.to_be_bytes()
}
}
impl From<u32> for Asn {
fn from(id: u32) -> Self {
Asn(id)
}
}
impl From<Asn> for u32 {
fn from(id: Asn) -> Self {
id.0
}
}

View File

@ -0,0 +1,46 @@
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IPAddrBlocks {
ips: Vec<IPAddressFamily>
}
// IP Address Family
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IPAddressFamily {
pub address_family: Afi,
pub ip_address_choice: IPAddressChoice,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Afi {
Ipv4,
Ipv6,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IPAddressChoice {
Inherit,
AddressOrRange(Vec<IPAddressOrRange>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IPAddressOrRange {
AddressPrefix(IPAddressPrefix),
AddressRange(IPAddressRange),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IPAddressPrefix {
pub address: IPAddress,
pub prefix_length: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IPAddressRange {
pub min: IPAddress,
pub max: IPAddress,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct IPAddress(u128);

View File

@ -0,0 +1,3 @@
pub(crate) mod ip_resources;
pub(crate) mod as_resources;
pub mod resource;

View File

@ -0,0 +1,10 @@
use crate::data_model::resources::as_resources::ASIdentifiers;
use crate::data_model::resources::ip_resources::IPAddrBlocks;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResourceSet {
ip_addr_blocks: IPAddrBlocks,
as_identifiers: ASIdentifiers,
}

27
src/data_model/ta.rs Normal file
View File

@ -0,0 +1,27 @@
use url::Url;
use x509_parser::prelude::*;
use crate::data_model::resources::resource::ResourceSet;
//
// #[derive(Debug, Clone)]
// pub struct TrustAnchorCert {
// /// 信任锚证书名称
// pub name: String,
//
// /// 证书原始DER内容
// pub cert_der: Vec<u8>,
//
// /// 证书
// pub cert: X509Certificate<'static>,
//
// /// 资源集合
// pub resources: ResourceSet,
//
// ///发布点
// pub publication_point: Url,
// }
//
// impl TrustAnchorCert {
//
// }

18
src/data_model/tal.rs Normal file
View File

@ -0,0 +1,18 @@
/// TAL Model
#[derive(Clone, Debug)]
pub struct Tal {
/// Optional human-readable comments
pub comments: Vec<String>,
/// Ordered list of URIs pointing to the TA certificate
pub uris: Vec<TalUri>,
/// SubjectPublicKeyInfo DER
pub spki_der: Vec<u8>,
}
#[derive(Debug, Clone)]
pub enum TalUri {
Rsync(String),
Https(String),
}