golem_certificate/
validator.rs

1use chrono::{DateTime, Utc};
2use hex::ToHex;
3use serde_json::Value;
4
5use crate::{
6    cryptography::{create_default_hash, verify_signature_json},
7    schemas::{
8        certificate::{
9            key_usage::validator::{validate_certificates_key_usage, validate_sign_node},
10            Certificate, Fingerprint,
11        },
12        node_descriptor::NodeDescriptor,
13        permissions::validator::validate_permissions,
14        signature::{SignedCertificate, SignedNodeDescriptor, Signer},
15        validity_period::validator::{validate_timestamp, validate_validity_period},
16        SIGNED_CERTIFICATE_SCHEMA_ID, SIGNED_NODE_DESCRIPTOR_SCHEMA_ID,
17    },
18    Error, Result,
19};
20
21use self::validated_data::{ValidatedCertificate, ValidatedNodeDescriptor};
22
23pub mod validated_data;
24
25/// Deserializes and validates certificate.
26/// # Arguments
27/// * `data` serialized certificate
28/// * `timestamp` optional timestamp to verify validity
29pub fn validate_certificate_str(
30    data: &str,
31    timestamp: Option<DateTime<Utc>>,
32) -> Result<ValidatedCertificate> {
33    let value: Value = serde_json::from_str(data).map_err(|e| Error::InvalidJson(e.to_string()))?;
34    validate_certificate(value, timestamp)
35}
36
37/// Validates certificate.
38/// # Arguments
39/// * `value` certificate
40/// * `timestamp` optional timestamp to verify validity
41pub fn validate_certificate(
42    value: Value,
43    timestamp: Option<DateTime<Utc>>,
44) -> Result<ValidatedCertificate> {
45    validate_schema(&value, SIGNED_CERTIFICATE_SCHEMA_ID, "certificate")?;
46    let signed_certificate: SignedCertificate = serde_json::from_value(value)
47        .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
48    let mut validated_certificate = validate_signed_certificate(&signed_certificate, timestamp)?;
49    validated_certificate
50        .certificate_chain_fingerprints
51        .reverse();
52    Ok(validated_certificate)
53}
54
55/// Deserializes and validates node descriptor.
56/// # Arguments
57/// * `data` serialized node descriptor
58/// * `timestamp` optional timestamp to verify validity
59pub fn validate_node_descriptor_str(
60    data: &str,
61    timestamp: Option<DateTime<Utc>>,
62) -> Result<ValidatedNodeDescriptor> {
63    let value: Value = serde_json::from_str(data).map_err(|e| Error::InvalidJson(e.to_string()))?;
64    validate_node_descriptor(value, timestamp)
65}
66
67/// Validates node descriptor.
68/// # Arguments
69/// * `value` node descriptor
70/// * `timestamp` optional timestamp to verify validity
71pub fn validate_node_descriptor(
72    value: Value,
73    timestamp: Option<DateTime<Utc>>,
74) -> Result<ValidatedNodeDescriptor> {
75    validate_schema(&value, SIGNED_NODE_DESCRIPTOR_SCHEMA_ID, "node descriptor")?;
76    let signed_node_descriptor: SignedNodeDescriptor = serde_json::from_value(value)
77        .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
78    let mut validated_node_descriptor =
79        validate_signed_node_descriptor(signed_node_descriptor, timestamp)?;
80    validated_node_descriptor
81        .certificate_chain_fingerprints
82        .reverse();
83    Ok(validated_node_descriptor)
84}
85
86fn validate_schema(value: &Value, schema_id: &str, structure_name: &str) -> Result<()> {
87    value["$schema"]
88        .as_str()
89        .map(|schema| {
90            if schema == schema_id {
91                Ok(())
92            } else {
93                Err(Error::UnsupportedSchema {
94                    schema: schema.to_owned(),
95                    structure_name: structure_name.to_owned(),
96                })
97            }
98        })
99        .unwrap_or_else(|| {
100            Err(Error::JsonDoesNotConformToSchema(format!(
101                "Missing `schema` property in {structure_name}"
102            )))
103        })
104}
105
106/// Validates signed node descriptor.
107/// # Arguments
108/// * `signed_node_descriptor`
109/// * `timestamp` optional timestamp to verify validity of the leaf certificate (last certificate in the chain).
110///    Validity periods of parent (issuer) certificates from the chain must fully include validity period of a child.
111fn validate_signed_node_descriptor(
112    signed_node_descriptor: SignedNodeDescriptor,
113    timestamp: Option<DateTime<Utc>>,
114) -> Result<ValidatedNodeDescriptor> {
115    let node_descriptor: NodeDescriptor =
116        serde_json::from_value(signed_node_descriptor.node_descriptor.clone())
117            .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
118
119    let signing_certificate = signed_node_descriptor.signature.signer;
120    let validated_certificate = validate_signed_certificate(&signing_certificate, None)?;
121
122    let leaf_certificate: Certificate = serde_json::from_value(signing_certificate.certificate)
123        .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
124    verify_signature_json(
125        &signed_node_descriptor.node_descriptor,
126        &signed_node_descriptor.signature.algorithm.encryption,
127        &signed_node_descriptor.signature.value,
128        &leaf_certificate.public_key,
129    )?;
130
131    validate_permissions(
132        &validated_certificate.permissions,
133        &node_descriptor.permissions,
134    )?;
135    validate_sign_node(&validated_certificate.key_usage)?;
136    validate_validity_period(
137        &validated_certificate.validity_period,
138        &node_descriptor.validity_period,
139    )?;
140
141    timestamp
142        .map(|ts| validate_timestamp(&node_descriptor.validity_period, ts))
143        .unwrap_or(Ok(()))?;
144
145    Ok(ValidatedNodeDescriptor {
146        certificate_chain_fingerprints: validated_certificate.certificate_chain_fingerprints,
147        permissions: node_descriptor.permissions,
148        node_id: node_descriptor.node_id,
149    })
150}
151
152fn create_certificate_fingerprint(signed_certificate: &SignedCertificate) -> Result<Fingerprint> {
153    create_fingerprint_for_value(&signed_certificate.certificate)
154}
155
156fn create_fingerprint_for_value(value: &Value) -> Result<Fingerprint> {
157    create_default_hash(value).map(|binary| binary.encode_hex())
158}
159
160/// Validates signed certificate.
161/// # Arguments
162/// * `signed_certificate`
163/// * `timestamp` optional timestamp to verify validity of the leaf certificate (last certificate in the chain).
164///    Validity periods of parent (issuer) certificates from the chain must fully include validity period of a child.
165fn validate_signed_certificate(
166    signed_certificate: &SignedCertificate,
167    timestamp: Option<DateTime<Utc>>,
168) -> Result<ValidatedCertificate> {
169    let parent = match &signed_certificate.signature.signer {
170        Signer::SelfSigned => {
171            let certificate: Certificate =
172                serde_json::from_value(signed_certificate.certificate.clone())
173                    .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
174            verify_signature_json(
175                &signed_certificate.certificate,
176                &signed_certificate.signature.algorithm.encryption,
177                &signed_certificate.signature.value,
178                &certificate.public_key,
179            )?;
180            ValidatedCertificate {
181                certificate_chain_fingerprints: vec![],
182                permissions: certificate.permissions,
183                key_usage: certificate.key_usage,
184                validity_period: certificate.validity_period,
185                subject: certificate.subject,
186            }
187        }
188        Signer::Certificate(signed_parent) => {
189            let parent: Certificate = serde_json::from_value(signed_parent.certificate.clone())
190                .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
191            verify_signature_json(
192                &signed_certificate.certificate,
193                &signed_certificate.signature.algorithm.encryption,
194                &signed_certificate.signature.value,
195                &parent.public_key,
196            )?;
197            validate_signed_certificate(signed_parent, None)?
198        }
199    };
200
201    let certificate: Certificate = serde_json::from_value(signed_certificate.certificate.clone())
202        .map_err(|e| Error::JsonDoesNotConformToSchema(e.to_string()))?;
203
204    validate_permissions(&parent.permissions, &certificate.permissions)?;
205    validate_certificates_key_usage(&parent.key_usage, &certificate.key_usage)?;
206    validate_validity_period(&parent.validity_period, &certificate.validity_period)?;
207    timestamp
208        .map(|ts| validate_timestamp(&certificate.validity_period, ts))
209        .unwrap_or(Ok(()))?;
210
211    let mut fingerprints = parent.certificate_chain_fingerprints;
212    fingerprints.push(create_certificate_fingerprint(signed_certificate)?);
213
214    Ok(ValidatedCertificate {
215        certificate_chain_fingerprints: fingerprints,
216        permissions: certificate.permissions,
217        key_usage: certificate.key_usage,
218        validity_period: certificate.validity_period,
219        subject: certificate.subject,
220    })
221}