1use std::borrow::Borrow;
2
3use base64::Engine;
4use rand::{thread_rng, CryptoRng, RngCore};
5use serde::Serialize;
6use serde_json::Value;
7use ssi_claims_core::SignatureError;
8use ssi_core::JsonPointer;
9use ssi_jws::JwsSigner;
10use ssi_jwt::JWTClaims;
11
12use crate::{
13 DecodedDisclosure, Disclosure, DisclosureDescription, SdAlg, SdJwtBuf, SdJwtPayload,
14 ARRAY_CLAIM_ITEM_PROPERTY_NAME, SD_CLAIM_NAME,
15};
16
17#[derive(Debug, thiserror::Error)]
19pub enum ConcealError {
20 #[error(transparent)]
22 Serialization(#[from] serde_json::Error),
23
24 #[error("concealed JSON value is not an object")]
26 NotAnObject,
27
28 #[error("cannot conceal root")]
30 CannotConcealRoot,
31
32 #[error("value not found")]
34 NotFound,
35
36 #[error("the `_sd` entry is not an array")]
38 SdEntryNotAnArray,
39}
40
41pub trait ConcealJwtClaims {
43 fn conceal(
45 &self,
46 sd_alg: SdAlg,
47 pointers: &[impl Borrow<JsonPointer>],
48 ) -> Result<(SdJwtPayload, Vec<DecodedDisclosure<'static>>), ConcealError>;
49
50 fn conceal_with(
52 &self,
53 sd_alg: SdAlg,
54 pointers: &[impl Borrow<JsonPointer>],
55 rng: impl CryptoRng + RngCore,
56 ) -> Result<(SdJwtPayload, Vec<DecodedDisclosure<'static>>), ConcealError>;
57
58 #[allow(async_fn_in_trait)]
60 async fn conceal_and_sign(
61 &self,
62 sd_alg: SdAlg,
63 pointers: &[impl Borrow<JsonPointer>],
64 signer: impl JwsSigner,
65 ) -> Result<SdJwtBuf, SignatureError>;
66
67 #[allow(async_fn_in_trait)]
69 async fn conceal_and_sign_with(
70 &self,
71 sd_alg: SdAlg,
72 pointers: &[impl Borrow<JsonPointer>],
73 signer: impl JwsSigner,
74 rng: impl CryptoRng + RngCore,
75 ) -> Result<SdJwtBuf, SignatureError>;
76}
77
78impl<T: Serialize> ConcealJwtClaims for JWTClaims<T> {
79 fn conceal(
80 &self,
81 sd_alg: SdAlg,
82 pointers: &[impl Borrow<JsonPointer>],
83 ) -> Result<(SdJwtPayload, Vec<DecodedDisclosure<'static>>), ConcealError> {
84 SdJwtPayload::conceal(self, sd_alg, pointers)
85 }
86
87 fn conceal_with(
88 &self,
89 sd_alg: SdAlg,
90 pointers: &[impl Borrow<JsonPointer>],
91 rng: impl CryptoRng + RngCore,
92 ) -> Result<(SdJwtPayload, Vec<DecodedDisclosure<'static>>), ConcealError> {
93 SdJwtPayload::conceal_with(self, sd_alg, pointers, rng)
94 }
95
96 async fn conceal_and_sign(
97 &self,
98 sd_alg: SdAlg,
99 pointers: &[impl Borrow<JsonPointer>],
100 signer: impl JwsSigner,
101 ) -> Result<SdJwtBuf, SignatureError> {
102 SdJwtBuf::conceal_and_sign(self, sd_alg, pointers, signer).await
103 }
104
105 async fn conceal_and_sign_with(
106 &self,
107 sd_alg: SdAlg,
108 pointers: &[impl Borrow<JsonPointer>],
109 signer: impl JwsSigner,
110 rng: impl CryptoRng + RngCore,
111 ) -> Result<SdJwtBuf, SignatureError> {
112 SdJwtBuf::conceal_and_sign_with(self, sd_alg, pointers, signer, rng).await
113 }
114}
115
116impl SdJwtPayload {
117 pub fn conceal<T: Serialize>(
120 value: &T,
121 sd_alg: SdAlg,
122 pointers: &[impl Borrow<JsonPointer>],
123 ) -> Result<(Self, Vec<DecodedDisclosure<'static>>), ConcealError> {
124 Self::conceal_with(value, sd_alg, pointers, thread_rng())
125 }
126
127 pub fn conceal_with<T: Serialize>(
130 value: &T,
131 sd_alg: SdAlg,
132 pointers: &[impl Borrow<JsonPointer>],
133 rng: impl CryptoRng + RngCore,
134 ) -> Result<(Self, Vec<DecodedDisclosure<'static>>), ConcealError> {
135 match serde_json::to_value(value)? {
136 Value::Object(obj) => Self::conceal_claims(obj, rng, sd_alg, pointers),
137 _ => Err(ConcealError::NotAnObject),
138 }
139 }
140
141 pub fn conceal_claims(
144 mut claims: serde_json::Map<String, Value>,
145 mut rng: impl CryptoRng + RngCore,
146 sd_alg: SdAlg,
147 pointers: &[impl Borrow<JsonPointer>],
148 ) -> Result<(Self, Vec<DecodedDisclosure<'static>>), ConcealError> {
149 let mut disclosures = Vec::with_capacity(pointers.len());
150
151 let mut sorted_pointers: Vec<_> = pointers.iter().map(Borrow::borrow).collect();
155 sorted_pointers.sort_unstable();
156
157 for pointer in sorted_pointers.into_iter().rev() {
158 disclosures.push(conceal_object_at(&mut claims, &mut rng, sd_alg, pointer)?);
159 }
160
161 let concealed = Self { sd_alg, claims };
162
163 Ok((concealed, disclosures))
164 }
165}
166
167fn generate_salt(rng: &mut (impl CryptoRng + RngCore)) -> String {
168 const DEFAULT_SALT_SIZE: usize = 128 / 8;
170 let mut salt_bytes = [0u8; DEFAULT_SALT_SIZE];
171 rng.fill_bytes(&mut salt_bytes);
172 base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(salt_bytes)
173}
174
175fn conceal_at(
176 value: &mut Value,
177 rng: &mut (impl CryptoRng + RngCore),
178 sd_alg: SdAlg,
179 pointer: &JsonPointer,
180) -> Result<DecodedDisclosure<'static>, ConcealError> {
181 match value {
182 Value::Object(object) => conceal_object_at(object, rng, sd_alg, pointer),
183 Value::Array(array) => conceal_array_at(array, rng, sd_alg, pointer),
184 _ => Err(ConcealError::CannotConcealRoot),
185 }
186}
187
188fn conceal_object_at(
189 object: &mut serde_json::Map<String, Value>,
190 rng: &mut (impl CryptoRng + RngCore),
191 sd_alg: SdAlg,
192 pointer: &JsonPointer,
193) -> Result<DecodedDisclosure<'static>, ConcealError> {
194 let (token, rest) = pointer
195 .split_first()
196 .ok_or(ConcealError::CannotConcealRoot)?;
197
198 let key = token.to_decoded();
199
200 if rest.is_empty() {
201 let value = object.remove(&*key).ok_or(ConcealError::NotFound)?;
202
203 let disclosure = DecodedDisclosure::from_parts(
204 generate_salt(rng),
205 DisclosureDescription::ObjectEntry {
206 key: key.into_owned(),
207 value,
208 },
209 );
210
211 add_disclosure(object, sd_alg, &disclosure.encoded)?;
212 Ok(disclosure)
213 } else {
214 let value = object.get_mut(&*key).ok_or(ConcealError::NotFound)?;
215
216 conceal_at(value, rng, sd_alg, rest)
217 }
218}
219
220fn conceal_array_at(
221 array: &mut [Value],
222 rng: &mut (impl CryptoRng + RngCore),
223 sd_alg: SdAlg,
224 pointer: &JsonPointer,
225) -> Result<DecodedDisclosure<'static>, ConcealError> {
226 let (token, rest) = pointer
227 .split_first()
228 .ok_or(ConcealError::CannotConcealRoot)?;
229
230 let i = token.as_array_index().ok_or(ConcealError::NotFound)?;
231
232 let value = array.get_mut(i).ok_or(ConcealError::NotFound)?;
233
234 if rest.is_empty() {
235 let disclosure = DecodedDisclosure::from_parts(
236 generate_salt(rng),
237 DisclosureDescription::ArrayItem(value.take()),
238 );
239
240 *value = new_concealed_array_item(sd_alg, &disclosure.encoded);
241 Ok(disclosure)
242 } else {
243 conceal_at(value, rng, sd_alg, pointer)
244 }
245}
246
247fn new_concealed_array_item(sd_alg: SdAlg, disclosure: &Disclosure) -> Value {
248 let mut object = serde_json::Map::new();
249 object.insert(
250 ARRAY_CLAIM_ITEM_PROPERTY_NAME.into(),
251 sd_alg.hash(disclosure).into(),
252 );
253 Value::Object(object)
254}
255
256fn add_disclosure(
257 object: &mut serde_json::Map<String, Value>,
258 sd_alg: SdAlg,
259 disclosure: &Disclosure,
260) -> Result<(), ConcealError> {
261 let sd = object
262 .entry(SD_CLAIM_NAME.to_owned())
263 .or_insert_with(|| Value::Array(Vec::new()))
264 .as_array_mut()
265 .ok_or(ConcealError::SdEntryNotAnArray)?;
266
267 sd.push(sd_alg.hash(disclosure).into());
268 Ok(())
269}