1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16
17 //! Arc related definitions and operations.
18
19 use crate::{
20 ag_err,
21 error::Error,
22 key::{AesKey, Identity, Nonce12},
23 traits::{AesGcm, Rng},
24 try_to_vec, FallibleAllocExt,
25 };
26 use alloc::vec::Vec;
27 use authgraph_wire::ErrorCode;
28 use coset::{
29 cbor::value::Value, iana, AsCborValue, CborSerializable, CoseEncrypt0, CoseEncrypt0Builder,
30 CoseError, HeaderBuilder, Label,
31 };
32 use zeroize::ZeroizeOnDrop;
33
34 /// Protected header label for permissions
35 pub const PERMISSIONS: Label = Label::Int(-70001);
36 /// Protected header label for timestamp added at the time of creating the arc
37 pub const TIMESTAMP: Label = Label::Int(-70003);
38 /// Protected header label for the nonce used for key agreement
39 pub const KE_NONCE: Label = Label::Int(-70004);
40 /// Protected header label for the type of the payload encrypted in the arc
41 pub const PAYLOAD_TYPE: Label = Label::Int(-70005);
42 /// Protected header label for the version of the payload encrypted in the arc, if applicable
43 pub const PAYLOAD_VERSION: Label = Label::Int(-70006);
44 /// Protected header label for sequence number, if used to prevent replay attacks
45 pub const SEQUENCE_NUMBER: Label = Label::Int(-70007);
46 /// Protected header label for direction which indicates the usage of the shared encryption key
47 pub const DIRECTION: Label = Label::Int(-70008);
48 /// Protected header label for "authentication completed" indicator used during key agreement
49 pub const AUTHENTICATION_COMPLETE: Label = Label::Int(-70009);
50 /// Protected header label for session id which is computed during key agreement
51 pub const SESSION_ID: Label = Label::Int(-70010);
52
53 /// Permission key for source identity
54 pub const SOURCE_ID_KEY: i32 = -4770552;
55 /// Permission key for sink identity
56 pub const SINK_ID_KEY: i32 = -4770553;
57 /// Permission key for `minting allowed`
58 pub const MINTING_ALLOWED_KEY: i32 = -4770555;
59 /// Permission key for `deleted on biometric change`
60 pub const DELETED_ON_BIOMETRIC_CHANGE_KEY: i32 = -4770556;
61
62 /// Authentication status of an arc containing the shared keys from an authenticated key agreement.
63 pub struct AuthenticationCompleted(pub bool);
64
65 /// Direction of shared encryption key usage
66 pub enum DirectionOfEncryption {
67 /// Incoming messages
68 In = 1,
69 /// Outgoing messages
70 Out = 2,
71 }
72
73 /// Payload of an arc in plain text
74 #[derive(Default, ZeroizeOnDrop)]
75 pub struct ArcPayload(pub Vec<u8>);
76
77 /// The structure containing the contents of an arc.
78 #[derive(Default)]
79 pub struct ArcContent {
80 /// Payload in plain text
81 pub payload: ArcPayload,
82 /// Protocol specific protected headers
83 pub protected_headers: Vec<(Label, Value)>,
84 /// Protocol specific unprotected headers
85 pub unprotected_headers: Vec<(Label, Value)>,
86 }
87
88 /// A function to create arcs given the inputs.
89 /// Note: This method only inserts the headers that are relevant for CoseEncrypt0
90 /// (e.g. algorithm, iv).
91 /// It is left up to the caller to insert all the other necessary protocol specific headers.
create_arc( encrypting_key: &AesKey, arc_content: ArcContent, cipher: &dyn AesGcm, rng: &mut dyn Rng, ) -> Result<Vec<u8>, Error>92 pub fn create_arc(
93 encrypting_key: &AesKey,
94 arc_content: ArcContent,
95 cipher: &dyn AesGcm,
96 rng: &mut dyn Rng,
97 ) -> Result<Vec<u8>, Error> {
98 // Create a nonce for the encryption operation
99 let nonce_for_enc = Nonce12::new(rng);
100 let mut protected_hdr = HeaderBuilder::new().algorithm(iana::Algorithm::A256GCM);
101 for (lbl, val) in arc_content.protected_headers {
102 match lbl {
103 Label::Int(int_lbl) => {
104 protected_hdr = protected_hdr.value(int_lbl, val);
105 }
106 Label::Text(txt_lbl) => {
107 protected_hdr = protected_hdr.text_value(txt_lbl, val);
108 }
109 }
110 }
111 let mut unprotected_hdr = HeaderBuilder::new();
112 unprotected_hdr = unprotected_hdr.iv(try_to_vec(&nonce_for_enc.0)?);
113
114 for (lbl, val) in arc_content.unprotected_headers {
115 match lbl {
116 Label::Int(int_lbl) => {
117 unprotected_hdr = unprotected_hdr.value(int_lbl, val);
118 }
119 Label::Text(txt_lbl) => {
120 unprotected_hdr = unprotected_hdr.text_value(txt_lbl, val);
121 }
122 }
123 }
124 let arc = CoseEncrypt0Builder::new()
125 .protected(protected_hdr.build())
126 .unprotected(unprotected_hdr.build())
127 .try_create_ciphertext(&arc_content.payload.0, &[], |input, aad| {
128 cipher.encrypt(encrypting_key, input, aad, &nonce_for_enc)
129 })?
130 .build();
131 Ok(arc.to_vec()?)
132 }
133
134 /// A function to deconstruct an encoded arc and return the arc content
decipher_arc( decrypting_key: &AesKey, arc: &[u8], cipher: &dyn AesGcm, ) -> Result<ArcContent, Error>135 pub fn decipher_arc(
136 decrypting_key: &AesKey,
137 arc: &[u8],
138 cipher: &dyn AesGcm,
139 ) -> Result<ArcContent, Error> {
140 let arc = CoseEncrypt0::from_slice(arc)?;
141 let nonce = Nonce12(
142 arc.unprotected
143 .iv
144 .clone()
145 .try_into()
146 .map_err(|e| ag_err!(InternalError, "failed to decode iv {:?}", e))?,
147 );
148 let payload = arc.decrypt(&[], |cipher_text, aad| {
149 cipher.decrypt(decrypting_key, cipher_text, aad, &nonce)
150 })?;
151 let (protected_headers, unprotected_headers) =
152 (arc.protected.header.rest, arc.unprotected.rest);
153 Ok(ArcContent { payload: ArcPayload(payload), protected_headers, unprotected_headers })
154 }
155
156 /// A structure encapsulating the (optional) permissions added to an arc as a protected header.
157 #[derive(Default)]
158 pub struct Permissions {
159 /// Identity of the source
160 pub source_id: Option<Identity>,
161 /// Identity of the sink
162 pub sink_id: Option<Identity>,
163 /// Set of identities to which the secret encrypted in the arc is allowed to be minted
164 pub minting_allowed: Option<Vec<Identity>>,
165 /// Indicates whether an auth key issued from a biometric TA is invalidated on new enrollment or
166 /// removal of biometrics.
167 pub deleted_on_biometric_change: Option<bool>,
168 }
169
170 impl AsCborValue for Permissions {
from_cbor_value(value: Value) -> Result<Self, CoseError>171 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
172 let permissions_vec: Vec<(Value, Value)> = match value {
173 Value::Map(permissions) => permissions,
174 _ => return Err(CoseError::UnexpectedItem("_", "Map")),
175 };
176 let mut permissions = Permissions::default();
177 for (key, val) in permissions_vec {
178 if let Value::Integer(k) = key {
179 if k == SOURCE_ID_KEY.into() {
180 let id = val.as_bytes().cloned().ok_or(CoseError::UnexpectedItem(
181 "None",
182 "Encoded source identity in the permissions",
183 ))?;
184 permissions.source_id = Some(Identity::from_slice(&id)?);
185 }
186 if k == SINK_ID_KEY.into() {
187 let id = val.as_bytes().cloned().ok_or(CoseError::UnexpectedItem(
188 "None",
189 "Encoded sink identity in the permissions",
190 ))?;
191 permissions.sink_id = Some(Identity::from_slice(&id)?);
192 }
193 if k == MINTING_ALLOWED_KEY.into() {
194 let id_array = val.as_array().cloned().ok_or(CoseError::UnexpectedItem(
195 "None",
196 "An array of encoded identity for minting allowed in the permissions",
197 ))?;
198 let mut allowed_ids = Vec::new();
199 for a in id_array {
200 let id = a.as_bytes().cloned().ok_or(CoseError::UnexpectedItem(
201 "None",
202 "An encoded identity for minting in the permissions",
203 ))?;
204 allowed_ids
205 .try_push(Identity::from_slice(&id)?)
206 .map_err(|_| CoseError::EncodeFailed)?;
207 }
208 permissions.minting_allowed = Some(allowed_ids);
209 }
210 if k == DELETED_ON_BIOMETRIC_CHANGE_KEY.into() {
211 permissions.deleted_on_biometric_change = val.as_bool();
212 }
213 }
214 }
215 Ok(permissions)
216 }
217
to_cbor_value(self) -> Result<Value, CoseError>218 fn to_cbor_value(self) -> Result<Value, CoseError> {
219 let mut cbor_permissions = Vec::<(Value, Value)>::new();
220 if let Some(source_id) = self.source_id {
221 let key = Value::Integer(SOURCE_ID_KEY.into());
222 let val = Value::Bytes(source_id.to_vec()?);
223 cbor_permissions.try_push((key, val)).map_err(|_| CoseError::EncodeFailed)?;
224 }
225 if let Some(sink_id) = self.sink_id {
226 let key = Value::Integer(SINK_ID_KEY.into());
227 let val = Value::Bytes(sink_id.to_vec()?);
228 cbor_permissions.try_push((key, val)).map_err(|_| CoseError::EncodeFailed)?;
229 }
230 if let Some(minting_allowed) = self.minting_allowed {
231 let key = Value::Integer(MINTING_ALLOWED_KEY.into());
232 let mut array = Vec::new();
233 for a in minting_allowed {
234 array.try_push(Value::Bytes(a.to_vec()?)).map_err(|_| CoseError::EncodeFailed)?;
235 }
236 let val = Value::Array(array);
237 cbor_permissions.try_push((key, val)).map_err(|_| CoseError::EncodeFailed)?;
238 }
239 if let Some(del_on_biometric_change) = self.deleted_on_biometric_change {
240 let key = Value::Integer(DELETED_ON_BIOMETRIC_CHANGE_KEY.into());
241 let val = Value::Bool(del_on_biometric_change);
242 cbor_permissions.try_push((key, val)).map_err(|_| CoseError::EncodeFailed)?;
243 }
244 Ok(Value::Map(cbor_permissions))
245 }
246 }
247