ssi_data_integrity_core/
canonicalization.rs

1use digest::Digest;
2use std::marker::PhantomData;
3
4use ssi_json_ld::{Expandable, JsonLdLoaderProvider, JsonLdNodeObject};
5use ssi_rdf::{AnyLdEnvironment, LdEnvironment};
6
7use crate::{
8    hashing::ConcatOutputSize,
9    suite::{
10        standard::{self, HashingAlgorithm, TransformationAlgorithm, TransformationError},
11        TransformationOptions,
12    },
13    CryptographicSuite, ProofConfigurationRef, SerializeCryptographicSuite,
14    StandardCryptographicSuite,
15};
16
17/// Canonical claims and configuration.
18pub struct CanonicalClaimsAndConfiguration {
19    pub claims: Vec<String>,
20    pub configuration: Vec<String>,
21}
22
23/// RDF Canonicalization transformation algorithm.
24pub struct CanonicalizeClaimsAndConfiguration;
25
26impl<S: CryptographicSuite> standard::TransformationAlgorithm<S>
27    for CanonicalizeClaimsAndConfiguration
28{
29    type Output = CanonicalClaimsAndConfiguration;
30}
31
32impl<S, T, C> standard::TypedTransformationAlgorithm<S, T, C> for CanonicalizeClaimsAndConfiguration
33where
34    S: SerializeCryptographicSuite,
35    T: JsonLdNodeObject + Expandable,
36    C: JsonLdLoaderProvider,
37{
38    async fn transform(
39        context: &C,
40        data: &T,
41        proof_configuration: ProofConfigurationRef<'_, S>,
42        _verification_method: &S::VerificationMethod,
43        _transformation_options: TransformationOptions<S>,
44    ) -> Result<Self::Output, TransformationError> {
45        let mut ld = LdEnvironment::default();
46
47        let expanded = data
48            .expand_with(&mut ld, context.loader())
49            .await
50            .map_err(|e| TransformationError::JsonLdExpansion(e.to_string()))?;
51
52        Ok(CanonicalClaimsAndConfiguration {
53            claims: ld
54                .canonical_form_of(&expanded)
55                .map_err(TransformationError::JsonLdDeserialization)?,
56            configuration: proof_configuration
57                .expand(context, data)
58                .await
59                .map_err(TransformationError::ProofConfigurationExpansion)?
60                .nquads_lines(),
61        })
62    }
63}
64
65pub struct HashCanonicalClaimsAndConfiguration<H>(PhantomData<H>);
66
67impl<H, S> HashingAlgorithm<S> for HashCanonicalClaimsAndConfiguration<H>
68where
69    H: Digest,
70    H::OutputSize: ConcatOutputSize,
71    S: StandardCryptographicSuite,
72    S::Transformation: TransformationAlgorithm<S, Output = CanonicalClaimsAndConfiguration>,
73{
74    type Output = <H::OutputSize as ConcatOutputSize>::ConcatOutput;
75
76    fn hash(
77        input: standard::TransformedData<S>,
78        _proof_configuration: ProofConfigurationRef<S>,
79        _verification_method: &S::VerificationMethod,
80    ) -> Result<Self::Output, standard::HashingError> {
81        let proof_configuration_hash = input
82            .configuration
83            .iter()
84            .fold(H::new(), |h, line| h.chain_update(line.as_bytes()))
85            .finalize();
86
87        let claims_hash = input
88            .claims
89            .iter()
90            .fold(H::new(), |h, line| h.chain_update(line.as_bytes()))
91            .finalize();
92
93        Ok(<H::OutputSize as ConcatOutputSize>::concat(
94            proof_configuration_hash,
95            claims_hash,
96        ))
97    }
98}
99
100pub struct ConcatCanonicalClaimsAndConfiguration;
101
102impl<S> HashingAlgorithm<S> for ConcatCanonicalClaimsAndConfiguration
103where
104    S: StandardCryptographicSuite,
105    S::Transformation: TransformationAlgorithm<S, Output = CanonicalClaimsAndConfiguration>,
106{
107    type Output = String;
108
109    fn hash(
110        input: standard::TransformedData<S>,
111        _proof_configuration: ProofConfigurationRef<S>,
112        _verification_method: &S::VerificationMethod,
113    ) -> Result<Self::Output, standard::HashingError> {
114        let mut result = String::new();
115
116        for line in &input.configuration {
117            result.push_str(line);
118        }
119
120        result.push('\n');
121
122        for line in &input.claims {
123            result.push_str(line);
124        }
125
126        Ok(result)
127    }
128}