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