bitwarden_ipc/crypto_provider/noise/
handshake.rs1use std::fmt::{Display, Formatter};
16
17use serde::{Deserialize, Serialize};
18
19use crate::crypto_provider::noise::transport_state::{
20 PersistentTransportState, SymmetricKey, TransportCipher,
21};
22
23#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
24pub(crate) enum CipherSuite {
25 #[allow(non_camel_case_types)]
26 Noise_NN_25519_ChaChaPoly_BLAKE2s,
27 #[allow(non_camel_case_types)]
28 Noise_NN_25519_AESGCM_SHA256,
29 #[allow(non_camel_case_types)]
30 #[default]
31 Noise_NN_P256_AESGCM_SHA256,
32}
33
34impl CipherSuite {
35 pub(crate) fn transport_cipher(&self) -> TransportCipher {
37 match self {
38 Self::Noise_NN_25519_ChaChaPoly_BLAKE2s => TransportCipher::ChaCha20Poly1305,
39 Self::Noise_NN_25519_AESGCM_SHA256 => TransportCipher::Aes256Gcm,
40 Self::Noise_NN_P256_AESGCM_SHA256 => TransportCipher::Aes256Gcm,
41 }
42 }
43}
44
45impl Display for CipherSuite {
46 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::Noise_NN_25519_ChaChaPoly_BLAKE2s => {
49 write!(f, "Noise_NN_25519_ChaChaPoly_BLAKE2s")
50 }
51 Self::Noise_NN_25519_AESGCM_SHA256 => {
52 write!(f, "Noise_NN_25519_AESGCM_SHA256")
53 }
54 Self::Noise_NN_P256_AESGCM_SHA256 => {
55 write!(f, "Noise_NN_P256_AESGCM_SHA256")
56 }
57 }
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub(crate) struct HandshakeStartMessage {
63 pub(super) ciphersuite: CipherSuite,
64 pub(super) noise_frame: Vec<u8>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(transparent)]
69pub(crate) struct HandshakeFinishMessage {
70 pub(super) noise_frame: Vec<u8>,
71}
72
73pub(crate) struct HandshakeInitiator {
74 ciphersuite: CipherSuite,
75 state: snow::HandshakeState,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub(crate) struct WriteError;
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub(crate) struct ReadError;
83
84impl HandshakeInitiator {
85 pub(crate) fn new(ciphersuite: &CipherSuite) -> Self {
86 let builder = snow::Builder::with_resolver(
87 ciphersuite
88 .to_string()
89 .parse()
90 .expect("Ciphersuite should be valid"),
91 Box::new(bitwarden_random::SdkCryptoResolver),
92 );
93 let handshake_state = builder
94 .build_initiator()
95 .expect("Handshake state should be buildable");
96 Self {
97 ciphersuite: *ciphersuite,
98 state: handshake_state,
99 }
100 }
101
102 pub(crate) fn write_start_message(&mut self) -> Result<HandshakeStartMessage, WriteError> {
103 let mut buf = [0u8; super::NOISE_MAX_MESSAGE_LEN];
104 let len = self
105 .state
106 .write_message(&[], &mut buf)
107 .map_err(|_| WriteError)?;
108 Ok(HandshakeStartMessage {
109 ciphersuite: self.ciphersuite,
110 noise_frame: buf[..len].to_vec(),
111 })
112 }
113
114 pub(crate) fn read_response_message(
115 &mut self,
116 message: &HandshakeFinishMessage,
117 ) -> Result<(), ReadError> {
118 let mut buf = [0u8; super::NOISE_MAX_MESSAGE_LEN];
119 self.state
120 .read_message(&message.noise_frame, &mut buf)
121 .map_err(|_| ReadError)?;
122 Ok(())
123 }
124}
125
126impl From<&mut HandshakeInitiator> for PersistentTransportState {
127 fn from(initiator: &mut HandshakeInitiator) -> Self {
128 let (i2r, r2i) = initiator.state.dangerously_get_raw_split();
129 PersistentTransportState::new(
130 SymmetricKey(i2r),
131 SymmetricKey(r2i),
132 initiator.ciphersuite.transport_cipher(),
133 )
134 }
135}
136
137pub(crate) struct HandshakeResponder {
138 ciphersuite: CipherSuite,
139 state: snow::HandshakeState,
140}
141
142impl HandshakeResponder {
143 pub(crate) fn new(ciphersuite: &CipherSuite) -> Self {
144 let builder = snow::Builder::with_resolver(
145 ciphersuite
146 .to_string()
147 .parse()
148 .expect("Ciphersuite should be valid"),
149 Box::new(bitwarden_random::SdkCryptoResolver),
150 );
151 let handshake_state = builder
152 .build_responder()
153 .expect("Handshake state should be buildable");
154 Self {
155 ciphersuite: *ciphersuite,
156 state: handshake_state,
157 }
158 }
159
160 pub(crate) fn read_start_message(
161 &mut self,
162 message: &HandshakeStartMessage,
163 ) -> Result<(), ReadError> {
164 let mut buf = [0u8; super::NOISE_MAX_MESSAGE_LEN];
165 self.state
166 .read_message(&message.noise_frame, &mut buf)
167 .map_err(|_| ReadError)?;
168 Ok(())
169 }
170
171 pub(crate) fn write_response_message(&mut self) -> Result<HandshakeFinishMessage, WriteError> {
172 let mut buf = [0u8; super::NOISE_MAX_MESSAGE_LEN];
173 let len = self
174 .state
175 .write_message(&[], &mut buf)
176 .map_err(|_| WriteError)?;
177 Ok(HandshakeFinishMessage {
178 noise_frame: buf[..len].to_vec(),
179 })
180 }
181}
182
183impl From<&mut HandshakeResponder> for PersistentTransportState {
184 fn from(responder: &mut HandshakeResponder) -> Self {
185 let (i2r, r2i) = responder.state.dangerously_get_raw_split();
186 PersistentTransportState::new(
187 SymmetricKey(r2i),
188 SymmetricKey(i2r),
189 responder.ciphersuite.transport_cipher(),
190 )
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::crypto_provider::noise::transport_state::assert_matching_pair;
198
199 fn run_handshake(ciphersuite: &CipherSuite) {
200 let mut initiator = HandshakeInitiator::new(ciphersuite);
201 let mut responder = HandshakeResponder::new(ciphersuite);
202
203 let init_message = initiator.write_start_message().unwrap();
204 responder.read_start_message(&init_message).unwrap();
205 let response_message = responder.write_response_message().unwrap();
206 initiator.read_response_message(&response_message).unwrap();
207
208 let initiator_transport_state = (&mut initiator).into();
209 let responder_transport_state = (&mut responder).into();
210 assert_matching_pair(&initiator_transport_state, &responder_transport_state);
211 }
212
213 #[test]
214 fn test_handshake() {
215 for ciphersuite in [
216 CipherSuite::Noise_NN_25519_ChaChaPoly_BLAKE2s,
217 CipherSuite::Noise_NN_25519_AESGCM_SHA256,
218 CipherSuite::Noise_NN_P256_AESGCM_SHA256,
219 ] {
220 run_handshake(&ciphersuite);
221 }
222 }
223}