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