golem_certificate/
validator.rs1use 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
25pub 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
37pub 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
55pub 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
67pub 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
106fn 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
160fn 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}