1 #![allow(missing_docs)] 2 // Copyright 2023 Google LLC 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 pub(crate) use crate::proto_adapter::{ 17 CipherCommitment, ClientFinished, ClientInit, GenericPublicKey, HandshakeCipher, 18 IntoAdapter as _, ServerInit, ToWrappedMessage as _, 19 }; 20 use crypto_provider::elliptic_curve::EphemeralSecret; 21 use crypto_provider::p256::{P256EcdhProvider, P256PublicKey, P256}; 22 use crypto_provider::x25519::X25519; 23 use crypto_provider::CryptoProvider; 24 use crypto_provider::{ 25 elliptic_curve::{EcdhProvider, PublicKey}, 26 hkdf::Hkdf, 27 sha2::{Sha256, Sha512}, 28 CryptoRng, 29 }; 30 use std::{ 31 collections::hash_set, 32 fmt::{self, Formatter}, 33 marker::PhantomData, 34 }; 35 use ukey2_proto::protobuf::Message; 36 use ukey2_proto::ukey2_all_proto::{securemessage, ukey}; 37 38 pub trait WireCompatibilityLayer { encode_public_key<C: CryptoProvider>( &self, key: Vec<u8>, cipher: HandshakeCipher, ) -> Option<Vec<u8>>39 fn encode_public_key<C: CryptoProvider>( 40 &self, 41 key: Vec<u8>, 42 cipher: HandshakeCipher, 43 ) -> Option<Vec<u8>>; decode_public_key<C: CryptoProvider>( &self, key: Vec<u8>, cipher: HandshakeCipher, ) -> Option<Vec<u8>>44 fn decode_public_key<C: CryptoProvider>( 45 &self, 46 key: Vec<u8>, 47 cipher: HandshakeCipher, 48 ) -> Option<Vec<u8>>; 49 } 50 51 #[derive(Clone)] 52 pub enum HandshakeImplementation { 53 /// Implementation of ukey2 exchange handshake according to the specs in 54 /// <https://github.com/google/ukey2/blob/master/README.md>. 55 /// 56 /// In particular, when encoding for the P256 public key, this uses the standardized encoding 57 /// described in [SEC 1](https://www.secg.org/sec1-v2.pdf). 58 /// 59 /// For X25519, the public key is the x-coordinate in little endian per RFC 7748. 60 Spec, 61 /// Implementation of ukey2 exchange handshake that matches 62 /// [the Java implementation](https://github.com/google/ukey2/blob/master/src/main/java/com/google/security/cryptauth/lib/securegcm/Ukey2Handshake.java), 63 /// but different from what the specs says. 64 /// 65 /// In particular, when encoding for the P256 curve, the public key is represented as serialized 66 /// bytes of the following proto: 67 /// ```text 68 /// message EcP256PublicKey { 69 /// // x and y are encoded in big-endian two's complement (slightly wasteful) 70 /// // Client MUST verify (x,y) is a valid point on NIST P256 71 /// required bytes x = 1; 72 /// required bytes y = 2; 73 /// } 74 /// ``` 75 /// 76 /// Encoding for X25519 is not supported in this mode. 77 PublicKeyInProtobuf, 78 } 79 80 impl WireCompatibilityLayer for HandshakeImplementation { encode_public_key<C: CryptoProvider>( &self, key: Vec<u8>, cipher: HandshakeCipher, ) -> Option<Vec<u8>>81 fn encode_public_key<C: CryptoProvider>( 82 &self, 83 key: Vec<u8>, 84 cipher: HandshakeCipher, 85 ) -> Option<Vec<u8>> { 86 match self { 87 HandshakeImplementation::Spec => Some(key), 88 HandshakeImplementation::PublicKeyInProtobuf => match cipher { 89 HandshakeCipher::P256Sha512 => { 90 let p256_key = 91 <C::P256 as P256EcdhProvider>::PublicKey::from_bytes(key.as_slice()) 92 .unwrap(); 93 let (x, y) = p256_key.to_affine_coordinates().unwrap(); 94 let bigboi_x = num_bigint::BigInt::from_biguint( 95 num_bigint::Sign::Plus, 96 num_bigint::BigUint::from_bytes_be(x.to_vec().as_slice()), 97 ); 98 let bigboi_y = num_bigint::BigInt::from_biguint( 99 num_bigint::Sign::Plus, 100 num_bigint::BigUint::from_bytes_be(y.to_vec().as_slice()), 101 ); 102 let proto_key = securemessage::EcP256PublicKey { 103 x: Some(bigboi_x.to_signed_bytes_be()), 104 y: Some(bigboi_y.to_signed_bytes_be()), 105 ..Default::default() 106 }; 107 let key = securemessage::GenericPublicKey { 108 type_: Some(securemessage::PublicKeyType::EC_P256.into()), 109 ec_p256_public_key: Some(proto_key).into(), 110 ..Default::default() 111 }; 112 key.write_to_bytes().ok() 113 } 114 HandshakeCipher::Curve25519Sha512 => None, 115 }, 116 } 117 } 118 decode_public_key<C: CryptoProvider>( &self, key: Vec<u8>, cipher: HandshakeCipher, ) -> Option<Vec<u8>>119 fn decode_public_key<C: CryptoProvider>( 120 &self, 121 key: Vec<u8>, 122 cipher: HandshakeCipher, 123 ) -> Option<Vec<u8>> { 124 match self { 125 HandshakeImplementation::Spec => Some(key), 126 HandshakeImplementation::PublicKeyInProtobuf => { 127 // key will be wrapped in a genericpublickey 128 let public_key: GenericPublicKey<C> = 129 securemessage::GenericPublicKey::parse_from_bytes(key.as_slice()) 130 .ok()? 131 .into_adapter() 132 .ok()?; 133 match public_key { 134 GenericPublicKey::Ec256(key) => { 135 debug_assert_eq!(cipher, HandshakeCipher::P256Sha512); 136 Some(key.to_bytes()) 137 } 138 } 139 } 140 } 141 } 142 } 143 144 pub struct Ukey2ServerStage1<C: CryptoProvider> { 145 pub(crate) next_protocols: hash_set::HashSet<String>, 146 pub(crate) handshake_impl: HandshakeImplementation, 147 _marker: PhantomData<C>, 148 } 149 150 impl<C: CryptoProvider> fmt::Debug for Ukey2ServerStage1<C> { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result151 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 152 write!(f, "Ukey2ServerS1") 153 } 154 } 155 156 impl<C: CryptoProvider> Ukey2ServerStage1<C> { from( next_protocols: hash_set::HashSet<String>, handshake_impl: HandshakeImplementation, ) -> Self157 pub fn from( 158 next_protocols: hash_set::HashSet<String>, 159 handshake_impl: HandshakeImplementation, 160 ) -> Self { 161 Self { 162 next_protocols, 163 handshake_impl, 164 _marker: PhantomData, 165 } 166 } 167 handle_client_init<R: rand::Rng + rand::CryptoRng>( self, rng: &mut R, client_init: ClientInit, client_init_msg_bytes: Vec<u8>, ) -> Result<Ukey2ServerStage2<C>, ClientInitError>168 pub(crate) fn handle_client_init<R: rand::Rng + rand::CryptoRng>( 169 self, 170 rng: &mut R, 171 client_init: ClientInit, 172 client_init_msg_bytes: Vec<u8>, 173 ) -> Result<Ukey2ServerStage2<C>, ClientInitError> { 174 if client_init.version() != &1 { 175 return Err(ClientInitError::BadVersion); 176 } 177 178 let next_protocol = client_init.next_protocol(); 179 if !self.next_protocols.contains(next_protocol) { 180 return Err(ClientInitError::BadNextProtocol); 181 } 182 183 // nothing to check here about client_init.random -- already been validated as 32 bytes 184 185 // all cipher types are supported, so no BAD_HANDSHAKE_CIPHER case 186 let commitment = client_init 187 .commitments() 188 .iter() 189 // we want to get the first matching cipher, but max_by_key returns the last max, 190 // so iterate in reverse direction 191 .rev() 192 // proto enum uses the priority as the numeric value 193 .max_by_key(|c| c.cipher().as_proto() as i32) 194 .ok_or(ClientInitError::BadHandshakeCipher)?; 195 match *commitment.cipher() { 196 // pick in priority order 197 HandshakeCipher::Curve25519Sha512 => { 198 let secret = ServerKeyPair::Curve25519( 199 <C::X25519 as EcdhProvider<X25519>>::EphemeralSecret::generate_random(&mut 200 <<<C::X25519 as EcdhProvider<X25519>>::EphemeralSecret as EphemeralSecret< 201 X25519, 202 >>::Rng as CryptoRng>::new(), 203 ), 204 ); 205 Ok(Ukey2ServerStage2::from( 206 &mut *rng, 207 client_init_msg_bytes, 208 commitment.clone(), 209 secret, 210 self.handshake_impl, 211 next_protocol.to_string(), 212 )) 213 } 214 HandshakeCipher::P256Sha512 => { 215 let secret = ServerKeyPair::P256( 216 <C::P256 as EcdhProvider<P256>>::EphemeralSecret::generate_random( 217 &mut<<<C::P256 as EcdhProvider<P256>>::EphemeralSecret as EphemeralSecret< 218 P256, 219 >>::Rng as CryptoRng>::new(), 220 ), 221 ); 222 Ok(Ukey2ServerStage2::from( 223 &mut *rng, 224 client_init_msg_bytes, 225 commitment.clone(), 226 secret, 227 self.handshake_impl, 228 next_protocol.to_string(), 229 )) 230 } 231 } 232 } 233 } 234 235 enum ServerKeyPair<C: CryptoProvider> { 236 Curve25519(<C::X25519 as EcdhProvider<X25519>>::EphemeralSecret), 237 P256(<C::P256 as EcdhProvider<P256>>::EphemeralSecret), 238 } 239 240 pub struct Ukey2ServerStage2<C: CryptoProvider> { 241 client_init_msg: Vec<u8>, 242 server_init_msg: Vec<u8>, 243 commitment: CipherCommitment, 244 key_pair: ServerKeyPair<C>, 245 pub(crate) handshake_impl: HandshakeImplementation, 246 next_protocol: String, 247 _marker: PhantomData<C>, 248 } 249 250 impl<C: CryptoProvider> fmt::Debug for Ukey2ServerStage2<C> { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result251 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 252 write!(f, "Ukey2ServerS2") 253 } 254 } 255 256 const HKDF_SALT_AUTH: &[u8] = b"UKEY2 v1 auth"; 257 const HKDF_SALT_NEXT: &[u8] = b"UKEY2 v1 next"; 258 259 impl<C: CryptoProvider> Ukey2ServerStage2<C> { from<R: rand::Rng + rand::CryptoRng>( rng: &mut R, client_init_msg: Vec<u8>, commitment: CipherCommitment, key_pair: ServerKeyPair<C>, handshake_impl: HandshakeImplementation, next_protocol: String, ) -> Self260 fn from<R: rand::Rng + rand::CryptoRng>( 261 rng: &mut R, 262 client_init_msg: Vec<u8>, 263 commitment: CipherCommitment, 264 key_pair: ServerKeyPair<C>, 265 handshake_impl: HandshakeImplementation, 266 next_protocol: String, 267 ) -> Self { 268 let random: [u8; 32] = rng.gen(); 269 let mut server_init = ukey::Ukey2ServerInit::default(); 270 server_init.set_version(1); 271 server_init.set_random(random.to_vec()); 272 server_init.set_handshake_cipher(commitment.cipher().as_proto()); 273 server_init.set_public_key(match &key_pair { 274 ServerKeyPair::Curve25519(es) => es.public_key_bytes(), 275 ServerKeyPair::P256(es) => handshake_impl 276 .encode_public_key::<C>(es.public_key_bytes(), HandshakeCipher::P256Sha512) 277 .unwrap(), 278 }); 279 280 Self { 281 client_init_msg, 282 server_init_msg: server_init.to_wrapped_msg().write_to_bytes().unwrap(), 283 commitment, 284 key_pair, 285 handshake_impl, 286 next_protocol, 287 _marker: PhantomData, 288 } 289 } 290 server_init_msg(&self) -> &[u8]291 pub fn server_init_msg(&self) -> &[u8] { 292 &self.server_init_msg 293 } 294 handle_client_finished_msg( self, msg: ClientFinished, client_finished_msg_bytes: &[u8], ) -> Result<Ukey2Server, ClientFinishedError>295 pub(crate) fn handle_client_finished_msg( 296 self, 297 msg: ClientFinished, 298 client_finished_msg_bytes: &[u8], 299 ) -> Result<Ukey2Server, ClientFinishedError> { 300 let hash_bytes = C::Sha512::sha512(client_finished_msg_bytes); 301 // must be constant time to avoid timing attack on hash equality 302 if C::constant_time_eq(hash_bytes.as_slice(), self.commitment.commitment()) { 303 // handshake is complete 304 // independently derive shared DH key 305 let shared_secret_bytes = match self.key_pair { 306 ServerKeyPair::Curve25519(es) => { 307 let buf = msg.public_key.into_iter().collect::<Vec<u8>>(); 308 let public_key: [u8; 32] = (&buf[..]) 309 .try_into() 310 .map_err(|_| ClientFinishedError::BadEd25519Key)?; 311 es.diffie_hellman( 312 &<C::X25519 as EcdhProvider<X25519>>::PublicKey::from_bytes(&public_key) 313 .map_err(|_| ClientFinishedError::BadEd25519Key)?, 314 ) 315 .map_err(|_| ClientFinishedError::BadKeyExchange)? 316 .into() 317 } 318 ServerKeyPair::P256(es) => { 319 let other_public_key = 320 &<C::P256 as P256EcdhProvider>::PublicKey::from_sec1_bytes( 321 self.handshake_impl 322 .decode_public_key::<C>(msg.public_key, HandshakeCipher::P256Sha512) 323 .ok_or(ClientFinishedError::BadP256Key)? 324 .as_slice(), 325 ) 326 .map_err(|_| ClientFinishedError::BadP256Key)?; 327 es.diffie_hellman(other_public_key) 328 .map_err(|_| ClientFinishedError::BadKeyExchange)? 329 .into() 330 } 331 }; 332 let shared_secret_sha256 = C::Sha256::sha256(&shared_secret_bytes).to_vec(); 333 Ok(Ukey2Server { 334 completed_handshake: CompletedHandshake::new( 335 self.client_init_msg, 336 self.server_init_msg, 337 shared_secret_sha256, 338 self.next_protocol, 339 ), 340 }) 341 } else { 342 Err(ClientFinishedError::UnknownCommitment) 343 } 344 } 345 } 346 347 /// Representation of the UKEY2 server information after the handshake has been completed. An 348 /// instance of this can be created by going through the handshake state machine (starting from 349 /// [`Ukey2ServerStage1`]). 350 pub struct Ukey2Server { 351 completed_handshake: CompletedHandshake, 352 } 353 354 impl fmt::Debug for Ukey2Server { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result355 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 356 write!(f, "Ukey2Server") 357 } 358 } 359 360 impl Ukey2Server { completed_handshake(&self) -> &CompletedHandshake361 pub fn completed_handshake(&self) -> &CompletedHandshake { 362 &self.completed_handshake 363 } 364 } 365 366 pub struct Ukey2ClientStage1<C: CryptoProvider> { 367 curve25519_secret: <C::X25519 as EcdhProvider<X25519>>::EphemeralSecret, 368 p256_secret: <C::P256 as EcdhProvider<P256>>::EphemeralSecret, 369 curve25519_client_finished_bytes: Vec<u8>, 370 p256_client_finished_bytes: Vec<u8>, 371 client_init_bytes: Vec<u8>, 372 commitment_ciphers: Vec<HandshakeCipher>, 373 handshake_impl: HandshakeImplementation, 374 next_protocol: String, 375 _marker: PhantomData<C>, 376 } 377 378 impl<C: CryptoProvider> fmt::Debug for Ukey2ClientStage1<C> { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result379 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 380 write!(f, "Ukey2Client1") 381 } 382 } 383 384 impl<C: CryptoProvider> Ukey2ClientStage1<C> { from<R: rand::Rng + rand::SeedableRng + rand::CryptoRng>( rng: &mut R, next_protocol: String, handshake_impl: HandshakeImplementation, ) -> Self385 pub fn from<R: rand::Rng + rand::SeedableRng + rand::CryptoRng>( 386 rng: &mut R, 387 next_protocol: String, 388 handshake_impl: HandshakeImplementation, 389 ) -> Self { 390 let random = rng.gen::<[u8; 32]>().to_vec(); 391 // Curve25519 ClientFinished Message 392 let curve25519_secret = 393 <C::X25519 as EcdhProvider<X25519>>::EphemeralSecret::generate_random( 394 &mut <<<C::X25519 as EcdhProvider<X25519>>::EphemeralSecret as EphemeralSecret< 395 X25519, 396 >>::Rng as CryptoRng>::new(), 397 ); 398 let curve25519_client_finished_bytes = { 399 let client_finished = ukey::Ukey2ClientFinished { 400 public_key: Some(curve25519_secret.public_key_bytes()), 401 ..Default::default() 402 }; 403 client_finished.to_wrapped_msg().write_to_bytes().unwrap() 404 }; 405 let curve25519_client_finished_hash = 406 C::Sha512::sha512(&curve25519_client_finished_bytes).to_vec(); 407 408 // P256 ClientFinished Message 409 let p256_secret = <C::P256 as EcdhProvider<P256>>::EphemeralSecret::generate_random( 410 &mut<<<C::P256 as EcdhProvider<P256>>::EphemeralSecret as EphemeralSecret< 411 P256, 412 >>::Rng as CryptoRng>::new(), 413 ); 414 let p256_client_finished_bytes = { 415 let client_finished = ukey::Ukey2ClientFinished { 416 public_key: Some( 417 handshake_impl 418 .encode_public_key::<C>( 419 p256_secret.public_key_bytes(), 420 HandshakeCipher::P256Sha512, 421 ) 422 .expect("Output of p256_secret.public_key_bytes should always be valid input for encode_public_key"), 423 ), 424 ..Default::default() 425 }; 426 client_finished.to_wrapped_msg().write_to_bytes().unwrap() 427 }; 428 let p256_client_finished_hash = C::Sha512::sha512(&p256_client_finished_bytes).to_vec(); 429 430 // ClientInit Message 431 let client_init_bytes = { 432 let curve25519_commitment = ukey::ukey2client_init::CipherCommitment { 433 handshake_cipher: Some(HandshakeCipher::Curve25519Sha512.as_proto().into()), 434 commitment: Some(curve25519_client_finished_hash), 435 ..Default::default() 436 }; 437 438 let p256_commitment = ukey::ukey2client_init::CipherCommitment { 439 handshake_cipher: Some(HandshakeCipher::P256Sha512.as_proto().into()), 440 commitment: Some(p256_client_finished_hash), 441 ..Default::default() 442 }; 443 444 let client_init = ukey::Ukey2ClientInit { 445 version: Some(1), 446 random: Some(random), 447 cipher_commitments: vec![curve25519_commitment, p256_commitment], 448 next_protocol: Some(next_protocol.to_string()), 449 ..Default::default() 450 }; 451 client_init.to_wrapped_msg().write_to_bytes().unwrap() 452 }; 453 454 Self { 455 curve25519_secret, 456 p256_secret, 457 curve25519_client_finished_bytes, 458 p256_client_finished_bytes, 459 client_init_bytes, 460 commitment_ciphers: vec![ 461 HandshakeCipher::Curve25519Sha512, 462 HandshakeCipher::P256Sha512, 463 ], 464 handshake_impl, 465 next_protocol, 466 _marker: PhantomData, 467 } 468 } 469 client_init_msg(&self) -> &[u8]470 pub fn client_init_msg(&self) -> &[u8] { 471 &self.client_init_bytes 472 } 473 handle_server_init( self, server_init: ServerInit, server_init_bytes: Vec<u8>, ) -> Result<Ukey2Client, ServerInitError>474 pub(crate) fn handle_server_init( 475 self, 476 server_init: ServerInit, 477 server_init_bytes: Vec<u8>, 478 ) -> Result<Ukey2Client, ServerInitError> { 479 if server_init.version() != &1 { 480 return Err(ServerInitError::BadVersion); 481 } 482 483 // loop over all commitments every time for a semblance of constant time-ness 484 let server_cipher = self 485 .commitment_ciphers 486 .iter() 487 .fold(None, |accum, c| { 488 if server_init.handshake_cipher() == c { 489 match accum { 490 None => Some(c), 491 Some(_) => accum, 492 } 493 } else { 494 accum 495 } 496 }) 497 .ok_or(ServerInitError::BadHandshakeCipher)?; 498 let (server_shared_secret, client_finished_bytes) = match server_cipher { 499 HandshakeCipher::P256Sha512 => { 500 let other_public_key = &<C::P256 as P256EcdhProvider>::PublicKey::from_sec1_bytes( 501 self.handshake_impl 502 .decode_public_key::<C>( 503 server_init.public_key.to_vec(), 504 HandshakeCipher::P256Sha512, 505 ) 506 .ok_or(ServerInitError::BadPublicKey)? 507 .as_slice(), 508 ) 509 .map_err(|_| ServerInitError::BadPublicKey)?; 510 let shared_secret = self 511 .p256_secret 512 .diffie_hellman(other_public_key) 513 .map_err(|_| ServerInitError::BadKeyExchange)?; 514 let shared_secret_bytes: [u8; 32] = shared_secret.into(); 515 (shared_secret_bytes, self.p256_client_finished_bytes) 516 } 517 HandshakeCipher::Curve25519Sha512 => { 518 let pub_key: [u8; 32] = server_init 519 .public_key 520 .try_into() 521 .map_err(|_| ServerInitError::BadPublicKey)?; 522 ( 523 self.curve25519_secret 524 .diffie_hellman( 525 &<C::X25519 as EcdhProvider<X25519>>::PublicKey::from_bytes(&pub_key) 526 .map_err(|_| ServerInitError::BadPublicKey)?, 527 ) 528 .map_err(|_| ServerInitError::BadKeyExchange)? 529 .into(), 530 self.curve25519_client_finished_bytes, 531 ) 532 } 533 }; 534 let shared_secret_bytes = C::Sha256::sha256(&server_shared_secret).to_vec(); 535 Ok(Ukey2Client { 536 client_finished_bytes, 537 completed_handshake: CompletedHandshake::new( 538 self.client_init_bytes, 539 server_init_bytes.to_vec(), 540 shared_secret_bytes, 541 self.next_protocol, 542 ), 543 }) 544 } 545 } 546 547 #[derive(Debug)] 548 #[allow(clippy::enum_variant_names)] 549 pub(crate) enum ServerInitError { 550 BadVersion, 551 BadHandshakeCipher, 552 BadPublicKey, 553 /// The diffie-hellman key exchange failed to generate a shared secret 554 BadKeyExchange, 555 } 556 557 #[derive(Clone)] 558 pub struct Ukey2Client { 559 completed_handshake: CompletedHandshake, 560 client_finished_bytes: Vec<u8>, 561 } 562 563 impl fmt::Debug for Ukey2Client { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result564 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 565 write!(f, "Ukey2Client") 566 } 567 } 568 569 impl Ukey2Client { client_finished_msg(&self) -> &[u8]570 pub fn client_finished_msg(&self) -> &[u8] { 571 &self.client_finished_bytes 572 } 573 completed_handshake(&self) -> &CompletedHandshake574 pub fn completed_handshake(&self) -> &CompletedHandshake { 575 &self.completed_handshake 576 } 577 } 578 579 #[allow(clippy::enum_variant_names)] 580 pub enum ClientInitError { 581 BadVersion, 582 BadHandshakeCipher, 583 BadNextProtocol, 584 } 585 586 pub enum ClientFinishedError { 587 BadEd25519Key, 588 BadP256Key, 589 UnknownCommitment, 590 /// The diffie-hellman key exchange failed to generate a shared secret 591 BadKeyExchange, 592 } 593 594 /// The result of completing the UKEY2 handshake. 595 #[derive(Clone)] 596 pub struct CompletedHandshake { 597 client_init_bytes: Vec<u8>, 598 server_init_bytes: Vec<u8>, 599 shared_secret: Vec<u8>, 600 pub next_protocol: String, 601 } 602 603 impl CompletedHandshake { new( client_init_bytes: Vec<u8>, server_init_bytes: Vec<u8>, shared_secret: Vec<u8>, next_protocol: String, ) -> Self604 fn new( 605 client_init_bytes: Vec<u8>, 606 server_init_bytes: Vec<u8>, 607 shared_secret: Vec<u8>, 608 next_protocol: String, 609 ) -> Self { 610 Self { 611 client_init_bytes, 612 server_init_bytes, 613 shared_secret, 614 next_protocol, 615 } 616 } 617 618 /// Returns an HKDF for the UKEY2 `AUTH_STRING`. auth_string<C: CryptoProvider>(&self) -> HandshakeHkdf<C>619 pub fn auth_string<C: CryptoProvider>(&self) -> HandshakeHkdf<C> { 620 HandshakeHkdf::new( 621 &self.client_init_bytes, 622 &self.server_init_bytes, 623 C::HkdfSha256::new(Some(HKDF_SALT_AUTH), &self.shared_secret), 624 ) 625 } 626 627 /// Returns an HKDF for the UKEY2 `NEXT_SECRET`. next_protocol_secret<C: CryptoProvider>(&self) -> HandshakeHkdf<C>628 pub fn next_protocol_secret<C: CryptoProvider>(&self) -> HandshakeHkdf<C> { 629 HandshakeHkdf::new( 630 &self.client_init_bytes, 631 &self.server_init_bytes, 632 C::HkdfSha256::new(Some(HKDF_SALT_NEXT), &self.shared_secret), 633 ) 634 } 635 } 636 637 /// A UKEY2 handshake secret that can derive output at the caller's preferred length. 638 pub struct HandshakeHkdf<'a, C: CryptoProvider> { 639 client_init_bytes: &'a [u8], 640 server_init_bytes: &'a [u8], 641 hkdf: C::HkdfSha256, 642 } 643 644 impl<'a, C: CryptoProvider> HandshakeHkdf<'a, C> { 645 /// Returns `None` if the requested size > 255 * 512 bytes. derive_array<const N: usize>(&self) -> Option<[u8; N]>646 pub fn derive_array<const N: usize>(&self) -> Option<[u8; N]> { 647 let mut buf = [0; N]; 648 self.derive_slice(&mut buf).map(|_| buf) 649 } 650 651 /// Returns `None` if the requested `length` > 255 * 512 bytes. derive_vec(&self, length: usize) -> Option<Vec<u8>>652 pub fn derive_vec(&self, length: usize) -> Option<Vec<u8>> { 653 let mut buf = vec![0; length]; 654 self.derive_slice(&mut buf).map(|_| buf) 655 } 656 657 /// Returns `None` if the provided `buf` has size > 255 * 512 bytes. derive_slice(&self, buf: &mut [u8]) -> Option<()>658 pub fn derive_slice(&self, buf: &mut [u8]) -> Option<()> { 659 self.hkdf 660 .expand_multi_info(&[self.client_init_bytes, self.server_init_bytes], buf) 661 .ok() 662 } 663 new(client_init_bytes: &'a [u8], server_init_bytes: &'a [u8], hkdf: C::HkdfSha256) -> Self664 fn new(client_init_bytes: &'a [u8], server_init_bytes: &'a [u8], hkdf: C::HkdfSha256) -> Self { 665 Self { 666 client_init_bytes, 667 server_init_bytes, 668 hkdf, 669 } 670 } 671 } 672