Skip to main content

x509_info/
summary.rs

1use crate::{
2    AlgorithmInfo, CertificateInfo, DistinguishedName, ExtensionDetails, KeyDataStatus,
3    NameAttribute, ParameterStatus, PssParameters, Validity,
4};
5
6/// Display-oriented name data without the complete Name DER.
7#[derive(Clone, Debug, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
10#[non_exhaustive]
11pub struct NameSummary {
12    /// Presentation string, not a normalized identity.
13    pub display: String,
14    /// Ordered RDN groups with repeated and unknown attributes preserved.
15    pub rdns: Vec<Vec<NameAttribute>>,
16}
17impl From<&DistinguishedName> for NameSummary {
18    fn from(name: &DistinguishedName) -> Self {
19        Self {
20            display: name.display.clone(),
21            rdns: name.rdns.clone(),
22        }
23    }
24}
25
26/// Algorithm description without raw parameter bytes; see AlgorithmInfo for full data.
27#[derive(Clone, Debug, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize))]
29#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
30#[non_exhaustive]
31pub struct AlgorithmSummary {
32    /// Dotted-decimal algorithm OID.
33    pub oid: String,
34    /// Recognized common label, or None.
35    pub name: Option<String>,
36    /// Parameter interpretation state; this is not a validation verdict.
37    pub parameter_status: ParameterStatus,
38    /// Decoded RSA-PSS parameters, when available.
39    pub pss: Option<PssParameters>,
40}
41impl From<&AlgorithmInfo> for AlgorithmSummary {
42    fn from(alg: &AlgorithmInfo) -> Self {
43        Self {
44            oid: alg.oid.clone(),
45            name: alg.name.clone(),
46            parameter_status: alg.parameter_status,
47            pss: alg.pss.clone(),
48        }
49    }
50}
51
52/// Public-key description without key/SPKI bytes.
53#[derive(Clone, Debug, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
56#[non_exhaustive]
57pub struct PublicKeySummary {
58    /// Algorithm and parameter description.
59    pub algorithm: AlgorithmSummary,
60    /// Supported key encoding status, independent of parameter status and trust.
61    pub key_data_status: KeyDataStatus,
62    /// Named-curve OID, when encoded as such.
63    pub curve_oid: Option<String>,
64    /// Recognized curve label, when available.
65    pub curve_name: Option<String>,
66    /// Modulus/nominal curve size, not a security-strength estimate.
67    pub key_size_bits: Option<usize>,
68}
69
70/// Extension summary without raw extnValue bytes; unsupported values remain explicit.
71#[derive(Clone, Debug, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize))]
73#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
74#[non_exhaustive]
75pub struct ExtensionSummary {
76    /// Dotted-decimal extension OID.
77    pub oid: String,
78    /// Encoded critical flag.
79    pub critical: bool,
80    /// Whether this OID occurs more than once in the certificate.
81    pub duplicate: bool,
82    /// Decoded fields, or an explicit unsupported/malformed state.
83    pub details: ExtensionDetails,
84}
85
86/// Owned certificate details for UI/FFI and optional Serde export.
87///
88/// Schema version 1 uses lowercase unseparated hex for byte strings, signed Unix
89/// seconds for certificate/private-key validity, dotted OIDs, null for absent/unknown
90/// values, and snake_case tagged enums. SCT timestamp_unix_ms retains unsigned
91/// Unix milliseconds from its wire format. Consumers must accept additional fields
92/// and unknown enum kinds.
93/// Complete DER, signature, key and extension bytes are omitted; use CertificateInfo
94/// for those. This summary is not a lossless certificate representation or trust verdict.
95#[derive(Clone, Debug, PartialEq, Eq)]
96#[cfg_attr(feature = "serde", derive(serde::Serialize))]
97#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
98#[non_exhaustive]
99pub struct CertificateSummary {
100    /// Serialization contract major version, currently 1 (separate from crate SemVer).
101    pub schema_version: u32,
102    /// One-based X.509 version.
103    pub version: u32,
104    /// Subject name and attributes.
105    pub subject: NameSummary,
106    /// Issuer name and attributes.
107    pub issuer: NameSummary,
108    /// Inclusive validity interval; no system clock is consulted.
109    pub validity: Validity,
110    /// Raw serial INTEGER content in lowercase hex, retaining sign padding.
111    pub serial_number_hex: String,
112    /// SHA-256 digest of the retained DER in lowercase hex.
113    pub sha256_fingerprint_hex: String,
114    /// Signature algorithm description.
115    pub signature_algorithm: AlgorithmSummary,
116    /// Inner signature algorithm, independently of the outer certificate field.
117    pub tbs_signature_algorithm: AlgorithmSummary,
118    /// Public-key description.
119    pub public_key: PublicKeySummary,
120    /// Extensions in encoded order, including unsupported/malformed/duplicate entries.
121    pub extensions: Vec<ExtensionSummary>,
122}
123
124impl CertificateInfo {
125    /// Copy application-facing fields into an owned summary and compute SHA-256.
126    /// Does not perform I/O or validation. The returned value outlives self and input.
127    /// Mutating the public full-result fields before calling this changes the summary;
128    /// derived fields are not re-parsed from DER. No result is cached.
129    pub fn summary(&self) -> CertificateSummary {
130        CertificateSummary {
131            schema_version: 1,
132            version: self.version,
133            subject: (&self.subject).into(),
134            issuer: (&self.issuer).into(),
135            validity: self.validity,
136            serial_number_hex: hex::encode(&self.serial_number),
137            sha256_fingerprint_hex: hex::encode(self.sha256_fingerprint()),
138            signature_algorithm: (&self.signature_algorithm).into(),
139            tbs_signature_algorithm: (&self.tbs_signature_algorithm).into(),
140            public_key: PublicKeySummary {
141                algorithm: (&self.public_key.algorithm).into(),
142                key_data_status: self.public_key.key_data_status,
143                curve_oid: self.public_key.curve_oid.clone(),
144                curve_name: self.public_key.curve_name.clone(),
145                key_size_bits: self.public_key.key_size_bits,
146            },
147            extensions: self
148                .extensions
149                .iter()
150                .map(|e| ExtensionSummary {
151                    oid: e.oid.clone(),
152                    critical: e.critical,
153                    duplicate: e.duplicate,
154                    details: e.details.clone(),
155                })
156                .collect(),
157        }
158    }
159}