xref: /aosp_15_r20/system/authgraph/core/src/ta.rs (revision 4185b0660fbe514985fdcf75410317caad8afad1)
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 //! Implementation of an AuthGraph trusted application (TA).
18 
19 use crate::{ag_err, error::Error, keyexchange};
20 use alloc::vec::Vec;
21 use authgraph_wire as wire;
22 use log::{debug, error, trace, warn};
23 use wire::{
24     cbor::AsCborValue, AuthenticationCompleteResponse, Code, CreateResponse, ErrorCode,
25     FinishResponse, InitResponse, PerformOpReq, PerformOpResponse, PerformOpRsp,
26 };
27 
28 /// Indication of which roles to support.
29 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
30 pub enum Role {
31     /// Act as (just) a source.
32     Source,
33     /// Act as (just) a sink.
34     Sink,
35     /// Act as both a source and a sink.
36     Both,
37 }
38 
39 impl Role {
40     /// Indicate whether role supports acting as a source.
is_source(&self) -> bool41     pub fn is_source(&self) -> bool {
42         match self {
43             Role::Source | Role::Both => true,
44             Role::Sink => false,
45         }
46     }
47     /// Indicate whether role supports acting as a sink.
is_sink(&self) -> bool48     pub fn is_sink(&self) -> bool {
49         match self {
50             Role::Sink | Role::Both => true,
51             Role::Source => false,
52         }
53     }
54 }
55 
56 /// AuthGraph trusted application instance.
57 pub struct AuthGraphTa {
58     ag_participant: keyexchange::AuthGraphParticipant,
59     role: Role,
60 }
61 
62 impl AuthGraphTa {
63     /// Create a TA instance using the provided trait implementations.
new(ag_participant: keyexchange::AuthGraphParticipant, role: Role) -> Self64     pub fn new(ag_participant: keyexchange::AuthGraphParticipant, role: Role) -> Self {
65         Self { ag_participant, role }
66     }
67 
68     /// Process a single serialized request, returning a serialized response.
process(&mut self, req_data: &[u8]) -> Vec<u8>69     pub fn process(&mut self, req_data: &[u8]) -> Vec<u8> {
70         let (req_code, rsp) = match PerformOpReq::from_slice(req_data) {
71             Ok(req) => {
72                 trace!("-> TA: received request {:?}", req.code());
73                 (Some(req.code()), self.process_req(req))
74             }
75             Err(e) => {
76                 error!("failed to decode CBOR request: {:?}", e);
77                 (None, error_rsp(ErrorCode::InternalError))
78             }
79         };
80         trace!("<- TA: send response {:?} rc {:?}", req_code, rsp.error_code);
81         match rsp.into_vec() {
82             Ok(rsp_data) => rsp_data,
83             Err(e) => {
84                 error!("failed to encode CBOR response: {:?}", e);
85                 invalid_cbor_rsp_data().to_vec()
86             }
87         }
88     }
89 
90     /// Process a single request, returning a [`PerformOpResponse`].
91     ///
92     /// Select the appropriate method based on the request type, and use the
93     /// request fields as parameters to the method.  In the opposite direction,
94     /// build a response message from the values returned by the method.
process_req(&mut self, req: PerformOpReq) -> PerformOpResponse95     fn process_req(&mut self, req: PerformOpReq) -> PerformOpResponse {
96         let code = req.code();
97         let result = match req {
98             PerformOpReq::Create(_req) => {
99                 self.create().map(|ret| PerformOpRsp::Create(CreateResponse { ret }))
100             }
101             PerformOpReq::Init(req) => self
102                 .init(&req.peer_pub_key, &req.peer_id, &req.peer_nonce, req.peer_version)
103                 .map(|ret| PerformOpRsp::Init(InitResponse { ret })),
104             PerformOpReq::Finish(req) => self
105                 .finish(
106                     &req.peer_pub_key,
107                     &req.peer_id,
108                     &req.peer_signature,
109                     &req.peer_nonce,
110                     req.peer_version,
111                     req.own_key,
112                 )
113                 .map(|ret| PerformOpRsp::Finish(FinishResponse { ret })),
114             PerformOpReq::AuthenticationComplete(req) => {
115                 self.auth_complete(&req.peer_signature, req.shared_keys).map(|ret| {
116                     PerformOpRsp::AuthenticationComplete(AuthenticationCompleteResponse { ret })
117                 })
118             }
119         };
120         match result {
121             Ok(rsp) => PerformOpResponse { error_code: ErrorCode::Ok, rsp: Some(rsp) },
122             Err(err) => {
123                 warn!("failing {:?} request with error {:?}", code, err);
124                 error_rsp(err.0)
125             }
126         }
127     }
128 
create(&mut self) -> Result<wire::SessionInitiationInfo, Error>129     fn create(&mut self) -> Result<wire::SessionInitiationInfo, Error> {
130         if !self.role.is_source() {
131             return Err(ag_err!(Unimplemented, "create() invoked on non-source"));
132         }
133         debug!("create()");
134         self.ag_participant.create()
135     }
init( &mut self, peer_pub_key: &[u8], peer_id: &[u8], peer_nonce: &[u8], peer_version: i32, ) -> Result<wire::KeInitResult, Error>136     fn init(
137         &mut self,
138         peer_pub_key: &[u8],
139         peer_id: &[u8],
140         peer_nonce: &[u8],
141         peer_version: i32,
142     ) -> Result<wire::KeInitResult, Error> {
143         if !self.role.is_sink() {
144             return Err(ag_err!(Unimplemented, "init() invoked on non-sink"));
145         }
146         debug!("init()");
147         self.ag_participant.init(peer_pub_key, peer_id, peer_nonce, peer_version)
148     }
finish( &mut self, peer_pub_key: &[u8], peer_id: &[u8], peer_signature: &[u8], peer_nonce: &[u8], peer_version: i32, own_key: wire::Key, ) -> Result<wire::SessionInfo, Error>149     fn finish(
150         &mut self,
151         peer_pub_key: &[u8],
152         peer_id: &[u8],
153         peer_signature: &[u8],
154         peer_nonce: &[u8],
155         peer_version: i32,
156         own_key: wire::Key,
157     ) -> Result<wire::SessionInfo, Error> {
158         if !self.role.is_source() {
159             return Err(ag_err!(Unimplemented, "finish() invoked on non-source"));
160         }
161         debug!("finish()");
162         self.ag_participant.finish(
163             peer_pub_key,
164             peer_id,
165             peer_signature,
166             peer_nonce,
167             peer_version,
168             own_key,
169         )
170     }
auth_complete( &mut self, peer_signature: &[u8], shared_keys: [Vec<u8>; 2], ) -> Result<[Vec<u8>; 2], Error>171     fn auth_complete(
172         &mut self,
173         peer_signature: &[u8],
174         shared_keys: [Vec<u8>; 2],
175     ) -> Result<[Vec<u8>; 2], Error> {
176         if !self.role.is_sink() {
177             return Err(ag_err!(Unimplemented, "auth_complete() invoked on non-sink"));
178         }
179         debug!("auth_complete()");
180         self.ag_participant.authentication_complete(peer_signature, shared_keys)
181     }
182 }
183 
184 /// Create an error outer response structure with the given error code.
error_rsp(error_code: ErrorCode) -> PerformOpResponse185 fn error_rsp(error_code: ErrorCode) -> PerformOpResponse {
186     PerformOpResponse { error_code, rsp: None }
187 }
188 
189 /// Hand-encoded [`PerformOpResponse`] data for [`ErrorCode::InternalError`].
190 /// Does not perform CBOR serialization (and so is suitable for error reporting if/when
191 /// CBOR serialization fails).
invalid_cbor_rsp_data() -> [u8; 3]192 fn invalid_cbor_rsp_data() -> [u8; 3] {
193     [
194         0x82, // 2-arr
195         0x2b, // nint, value -12
196         0x80, // 0-arr
197     ]
198 }
199 
200 #[cfg(test)]
201 mod tests {
202     use super::*;
203 
204     #[test]
test_invalid_data()205     fn test_invalid_data() {
206         // Cross-check that the hand-encoded invalid CBOR data matches an auto-encoded equivalent.
207         let rsp = error_rsp(ErrorCode::InternalError);
208         let rsp_data = rsp.into_vec().unwrap();
209         assert_eq!(rsp_data, invalid_cbor_rsp_data());
210     }
211 }
212