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 //! An adapter that converts between generated protobuf types and native Rust types.
16 
17 use crypto_provider::elliptic_curve::EcdhProvider;
18 use crypto_provider::p256::{P256EcdhProvider, P256PublicKey, P256};
19 use crypto_provider::CryptoProvider;
20 use std::collections::HashSet;
21 use std::fmt::{Display, Formatter};
22 use ukey2_proto::ukey2_all_proto::{securemessage, ukey};
23 
24 /// For generated proto types for UKEY2 messages
25 trait WithMessageType: ukey2_proto::protobuf::Message {
msg_type() -> ukey::ukey2message::Type26     fn msg_type() -> ukey::ukey2message::Type;
27 }
28 
29 pub(crate) trait ToWrappedMessage {
30     /// Wrap `self` in a `Ukey2Message`. Creates a new `Ukey2Message` with `message_type` set to
31     /// [`msg_type`][WithMessageType::msg_type] and `message_data` set to the serialized bytes for
32     /// the `self` proto.
to_wrapped_msg(self) -> ukey::Ukey2Message33     fn to_wrapped_msg(self) -> ukey::Ukey2Message;
34 }
35 
36 impl<M: WithMessageType> ToWrappedMessage for M {
to_wrapped_msg(self) -> ukey::Ukey2Message37     fn to_wrapped_msg(self) -> ukey::Ukey2Message {
38         ukey::Ukey2Message {
39             message_type: Some(Self::msg_type().into()),
40             message_data: self.write_to_bytes().ok(),
41             ..Default::default()
42         }
43     }
44 }
45 
46 impl WithMessageType for ukey::Ukey2Alert {
msg_type() -> ukey::ukey2message::Type47     fn msg_type() -> ukey::ukey2message::Type {
48         ukey::ukey2message::Type::ALERT
49     }
50 }
51 
52 impl WithMessageType for ukey::Ukey2ServerInit {
msg_type() -> ukey::ukey2message::Type53     fn msg_type() -> ukey::ukey2message::Type {
54         ukey::ukey2message::Type::SERVER_INIT
55     }
56 }
57 
58 impl WithMessageType for ukey::Ukey2ClientFinished {
msg_type() -> ukey::ukey2message::Type59     fn msg_type() -> ukey::ukey2message::Type {
60         ukey::ukey2message::Type::CLIENT_FINISH
61     }
62 }
63 
64 impl WithMessageType for ukey::Ukey2ClientInit {
msg_type() -> ukey::ukey2message::Type65     fn msg_type() -> ukey::ukey2message::Type {
66         ukey::ukey2message::Type::CLIENT_INIT
67     }
68 }
69 
70 /// Convert a generated proto type into our custom adapter type.
71 pub(crate) trait IntoAdapter<A> {
72     /// Convert `self` into the adapter type.
into_adapter(self) -> Result<A, ukey::ukey2alert::AlertType>73     fn into_adapter(self) -> Result<A, ukey::ukey2alert::AlertType>;
74 }
75 
76 /// Enum representing the different supported next_protocol strings, ordered by desirability.
77 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
78 #[repr(i32)]
79 pub enum NextProtocol {
80     /// AES-256-GCM-SIV, for use with newer clients.
81     Aes256GcmSiv,
82     /// AES_256_CBC-HMAC_SHA256, already in use and supported by all clients.
83     Aes256CbcHmacSha256,
84 }
85 
86 impl TryFrom<&String> for NextProtocol {
87     type Error = ukey::ukey2alert::AlertType;
88 
try_from(value: &String) -> Result<Self, Self::Error>89     fn try_from(value: &String) -> Result<Self, Self::Error> {
90         match value.as_str() {
91             "AES_256_GCM_SIV" => Ok(NextProtocol::Aes256GcmSiv),
92             "AES_256_CBC-HMAC_SHA256" => Ok(NextProtocol::Aes256CbcHmacSha256),
93             _ => Err(ukey::ukey2alert::AlertType::BAD_NEXT_PROTOCOL),
94         }
95     }
96 }
97 
98 impl Display for NextProtocol {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result99     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100         write!(
101             f,
102             "{}",
103             match self {
104                 NextProtocol::Aes256CbcHmacSha256 => "AES_256_CBC-HMAC_SHA256",
105                 NextProtocol::Aes256GcmSiv => "AES_256_GCM_SIV",
106             },
107         )
108     }
109 }
110 
111 #[derive(Debug, PartialEq, Eq)]
112 pub(crate) enum MessageType {
113     ClientInit,
114     ServerInit,
115     ClientFinish,
116 }
117 
118 pub(crate) struct ClientInit {
119     version: i32,
120     commitments: Vec<CipherCommitment>,
121     next_protocols: HashSet<NextProtocol>,
122 }
123 
124 impl ClientInit {
version(&self) -> i32125     pub fn version(&self) -> i32 {
126         self.version
127     }
128 
commitments(&self) -> &[CipherCommitment]129     pub fn commitments(&self) -> &[CipherCommitment] {
130         &self.commitments
131     }
132 
next_protocols(&self) -> &HashSet<NextProtocol>133     pub fn next_protocols(&self) -> &HashSet<NextProtocol> {
134         &self.next_protocols
135     }
136 }
137 
138 #[allow(dead_code)]
139 pub(crate) struct ServerInit {
140     version: i32,
141     random: [u8; 32],
142     handshake_cipher: HandshakeCipher,
143     pub(crate) public_key: Vec<u8>,
144     selected_next_protocol: NextProtocol,
145 }
146 
147 impl ServerInit {
version(&self) -> i32148     pub fn version(&self) -> i32 {
149         self.version
150     }
151 
handshake_cipher(&self) -> HandshakeCipher152     pub fn handshake_cipher(&self) -> HandshakeCipher {
153         self.handshake_cipher
154     }
155 
selected_next_protocol(&self) -> NextProtocol156     pub fn selected_next_protocol(&self) -> NextProtocol {
157         self.selected_next_protocol
158     }
159 }
160 
161 pub(crate) struct ClientFinished {
162     pub(crate) public_key: Vec<u8>,
163 }
164 
165 /// The handshake cipher used for UKEY2 handshake. Corresponds to the proto message
166 /// `ukey::Ukey2HandshakeCipher`.
167 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
168 pub enum HandshakeCipher {
169     /// NIST P-256 used for ECDH, SHA512 used for commitment
170     P256Sha512,
171     /// Curve 25519 used for ECDH, SHA512 used for commitment
172     Curve25519Sha512,
173 }
174 
175 impl HandshakeCipher {
as_proto(&self) -> ukey::Ukey2HandshakeCipher176     pub(crate) fn as_proto(&self) -> ukey::Ukey2HandshakeCipher {
177         match self {
178             HandshakeCipher::P256Sha512 => ukey::Ukey2HandshakeCipher::P256_SHA512,
179             HandshakeCipher::Curve25519Sha512 => ukey::Ukey2HandshakeCipher::CURVE25519_SHA512,
180         }
181     }
182 }
183 
184 #[derive(Clone)]
185 pub(crate) struct CipherCommitment {
186     cipher: HandshakeCipher,
187     commitment: Vec<u8>,
188 }
189 
190 impl CipherCommitment {
cipher(&self) -> HandshakeCipher191     pub fn cipher(&self) -> HandshakeCipher {
192         self.cipher
193     }
194 
commitment(&self) -> &[u8]195     pub fn commitment(&self) -> &[u8] {
196         &self.commitment
197     }
198 }
199 
200 pub(crate) enum GenericPublicKey<C: CryptoProvider> {
201     Ec256(<C::P256 as EcdhProvider<P256>>::PublicKey),
202     // Other public key types are not supported
203 }
204 
205 impl IntoAdapter<MessageType> for ukey::ukey2message::Type {
into_adapter(self) -> Result<MessageType, ukey::ukey2alert::AlertType>206     fn into_adapter(self) -> Result<MessageType, ukey::ukey2alert::AlertType> {
207         match self {
208             ukey::ukey2message::Type::CLIENT_INIT => Ok(MessageType::ClientInit),
209             ukey::ukey2message::Type::SERVER_INIT => Ok(MessageType::ServerInit),
210             ukey::ukey2message::Type::CLIENT_FINISH => Ok(MessageType::ClientFinish),
211             _ => Err(ukey::ukey2alert::AlertType::BAD_MESSAGE_TYPE),
212         }
213     }
214 }
215 
216 impl IntoAdapter<HandshakeCipher> for i32 {
into_adapter(self) -> Result<HandshakeCipher, ukey::ukey2alert::AlertType>217     fn into_adapter(self) -> Result<HandshakeCipher, ukey::ukey2alert::AlertType> {
218         const P256_CODE: i32 = ukey::Ukey2HandshakeCipher::P256_SHA512 as i32;
219         const CURVE25519_CODE: i32 = ukey::Ukey2HandshakeCipher::CURVE25519_SHA512 as i32;
220         match self {
221             P256_CODE => Ok(HandshakeCipher::P256Sha512),
222             CURVE25519_CODE => Ok(HandshakeCipher::Curve25519Sha512),
223             _ => Err(ukey::ukey2alert::AlertType::BAD_HANDSHAKE_CIPHER),
224         }
225     }
226 }
227 
228 impl IntoAdapter<CipherCommitment> for ukey::ukey2client_init::CipherCommitment {
into_adapter(self) -> Result<CipherCommitment, ukey::ukey2alert::AlertType>229     fn into_adapter(self) -> Result<CipherCommitment, ukey::ukey2alert::AlertType> {
230         let handshake_cipher: HandshakeCipher = self
231             .handshake_cipher
232             .ok_or(ukey::ukey2alert::AlertType::BAD_HANDSHAKE_CIPHER)
233             .and_then(|code| code.value().into_adapter())?;
234         // no bad commitment so this is best-effort
235         let commitment = self
236             .commitment
237             .filter(|c| !c.is_empty())
238             .ok_or(ukey::ukey2alert::AlertType::BAD_HANDSHAKE_CIPHER)?;
239         Ok(CipherCommitment { commitment, cipher: handshake_cipher })
240     }
241 }
242 
243 impl IntoAdapter<ClientInit> for ukey::Ukey2ClientInit {
into_adapter(self) -> Result<ClientInit, ukey::ukey2alert::AlertType>244     fn into_adapter(self) -> Result<ClientInit, ukey::ukey2alert::AlertType> {
245         if self.random().len() != 32 {
246             return Err(ukey::ukey2alert::AlertType::BAD_RANDOM);
247         }
248         let version: i32 = self.version.ok_or(ukey::ukey2alert::AlertType::BAD_VERSION)?;
249         let next_protocol = self
250             .next_protocol
251             .filter(|n| !n.is_empty())
252             .ok_or(ukey::ukey2alert::AlertType::BAD_NEXT_PROTOCOL)?;
253         let mut next_protocols: HashSet<NextProtocol> =
254             HashSet::from([(&next_protocol).try_into()?]);
255         let other_next_protocols: Vec<NextProtocol> =
256             self.next_protocols.iter().filter_map(|p| p.try_into().ok()).collect();
257         next_protocols.extend(&other_next_protocols);
258         Ok(ClientInit {
259             next_protocols,
260             version,
261             commitments: self
262                 .cipher_commitments
263                 .into_iter()
264                 .map(|c| c.into_adapter())
265                 .collect::<Result<Vec<_>, _>>()?,
266         })
267     }
268 }
269 
270 impl IntoAdapter<ServerInit> for ukey::Ukey2ServerInit {
into_adapter(self) -> Result<ServerInit, ukey::ukey2alert::AlertType>271     fn into_adapter(self) -> Result<ServerInit, ukey::ukey2alert::AlertType> {
272         let version: i32 = self.version.ok_or(ukey::ukey2alert::AlertType::BAD_VERSION)?;
273         let random: [u8; 32] = self
274             .random
275             .and_then(|r| r.try_into().ok())
276             .ok_or(ukey::ukey2alert::AlertType::BAD_RANDOM)?;
277         let handshake_cipher = self
278             .handshake_cipher
279             .ok_or(ukey::ukey2alert::AlertType::BAD_HANDSHAKE_CIPHER)
280             .and_then(|code| code.value().into_adapter())?;
281         // We will be handling bad pubkeys in the layers above
282         let public_key: Vec<u8> =
283             self.public_key.ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
284         let selected_next_protocol = self
285             .selected_next_protocol
286             .and_then(|p| (&p).try_into().ok())
287             .unwrap_or(NextProtocol::Aes256CbcHmacSha256);
288         Ok(ServerInit { handshake_cipher, version, public_key, random, selected_next_protocol })
289     }
290 }
291 
292 impl IntoAdapter<ClientFinished> for ukey::Ukey2ClientFinished {
into_adapter(self) -> Result<ClientFinished, ukey::ukey2alert::AlertType>293     fn into_adapter(self) -> Result<ClientFinished, ukey::ukey2alert::AlertType> {
294         let public_key: Vec<u8> =
295             self.public_key.ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
296         Ok(ClientFinished { public_key })
297     }
298 }
299 
300 impl<C: CryptoProvider> IntoAdapter<GenericPublicKey<C>> for securemessage::GenericPublicKey {
into_adapter(self) -> Result<GenericPublicKey<C>, ukey::ukey2alert::AlertType>301     fn into_adapter(self) -> Result<GenericPublicKey<C>, ukey::ukey2alert::AlertType> {
302         let key_type = self
303             .type_
304             .and_then(|t| t.enum_value().ok())
305             .ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
306         match key_type {
307             securemessage::PublicKeyType::EC_P256 => {
308                 let (key_x, key_y) = self
309                     .ec_p256_public_key
310                     .into_option()
311                     .and_then(|pk| pk.x.zip(pk.y))
312                     .ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
313                 let key_x_bytes: [u8; 32] = positive_twos_complement_to_32_byte_unsigned(&key_x)
314                     .ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
315                 let key_y_bytes: [u8; 32] = positive_twos_complement_to_32_byte_unsigned(&key_y)
316                     .ok_or(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)?;
317                 <C::P256 as P256EcdhProvider>::PublicKey::from_affine_coordinates(
318                     &key_x_bytes,
319                     &key_y_bytes,
320                 )
321                 .map(GenericPublicKey::Ec256)
322                 .map_err(|_| ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)
323             }
324             securemessage::PublicKeyType::RSA2048 => {
325                 // We don't support RSA keys
326                 Err(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)
327             }
328             securemessage::PublicKeyType::DH2048_MODP => {
329                 // We don't support DH2048 keys, only ECDH.
330                 Err(ukey::ukey2alert::AlertType::BAD_PUBLIC_KEY)
331             }
332         }
333     }
334 }
335 
336 /// Turns a big endian two's complement integer representation into big endian unsigned
337 /// representation. If the input byte array is not positive or cannot be fit into 32 byte unsigned
338 /// int range, then `None` is returned.
positive_twos_complement_to_32_byte_unsigned(twos_complement: &[u8]) -> Option<[u8; 32]>339 fn positive_twos_complement_to_32_byte_unsigned(twos_complement: &[u8]) -> Option<[u8; 32]> {
340     #[allow(clippy::indexing_slicing)]
341     if !twos_complement.is_empty() && (twos_complement[0] & 0x80) == 0 {
342         let mut twos_complement_iter = twos_complement.iter().rev();
343         let mut result = [0_u8; 32];
344         for (dst, src) in result.iter_mut().rev().zip(&mut twos_complement_iter) {
345             *dst = *src;
346         }
347         if twos_complement_iter.any(|x| *x != 0) {
348             // If any remaining elements are non-zero, the input cannot be fit into the 32 byte
349             // unsigned range
350             return None;
351         }
352         // No conversion needed since positive two's complement is the same as unsigned
353         Some(result)
354     } else {
355         None
356     }
357 }
358 
359 #[cfg(test)]
360 mod test {
361     #[test]
test_positive_twos_complement_to_32_byte_unsigned()362     fn test_positive_twos_complement_to_32_byte_unsigned() {
363         assert_eq!(
364             super::positive_twos_complement_to_32_byte_unsigned(&[]), // Empty input
365             None
366         );
367         assert_eq!(
368             super::positive_twos_complement_to_32_byte_unsigned(&[0xff, 0x05, 0x05]), // Negative
369             None
370         );
371         assert_eq!(
372             super::positive_twos_complement_to_32_byte_unsigned(&[0xff; 32]), // Negative
373             None
374         );
375         assert_eq!(
376             super::positive_twos_complement_to_32_byte_unsigned(&[0x05; 34]), // Too long
377             None
378         );
379         assert_eq!(
380             super::positive_twos_complement_to_32_byte_unsigned(&[0x05, 0xff]),
381             Some([
382                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384                 0x00, 0x00, 0x05, 0xff
385             ])
386         );
387         assert_eq!(
388             super::positive_twos_complement_to_32_byte_unsigned(&[0x05, 0x05, 0x05]),
389             Some([
390                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
391                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392                 0x00, 0x05, 0x05, 0x05
393             ])
394         );
395         assert_eq!(
396             super::positive_twos_complement_to_32_byte_unsigned(&[0x05; 32]),
397             Some([0x05; 32])
398         );
399         let mut input_33_bytes = [0xff_u8; 33];
400         assert_eq!(
401             super::positive_twos_complement_to_32_byte_unsigned(&input_33_bytes),
402             None // Negative input
403         );
404         input_33_bytes[0] = 0;
405         assert_eq!(
406             super::positive_twos_complement_to_32_byte_unsigned(&input_33_bytes),
407             Some([0xff; 32])
408         );
409     }
410 }
411