Worker side of the mutual authentication. Called on the accepted TCP stream before reading any Cake protocol messages.
(stream: &mut S, key: &str)
| 86 | /// |
| 87 | /// Called on the accepted TCP stream before reading any Cake protocol messages. |
| 88 | pub async fn authenticate_as_worker<S>(stream: &mut S, key: &str) -> Result<()> |
| 89 | where |
| 90 | S: AsyncReadExt + AsyncWriteExt + Unpin, |
| 91 | { |
| 92 | let key_bytes = key.as_bytes(); |
| 93 | |
| 94 | // Step 1: read master's nonce |
| 95 | let mut master_nonce = [0u8; NONCE_SIZE]; |
| 96 | stream.read_exact(&mut master_nonce).await?; |
| 97 | |
| 98 | // Step 2: send HMAC response + our nonce in one write |
| 99 | let worker_hmac = compute_hmac(key_bytes, &master_nonce); |
| 100 | let worker_nonce = random_nonce(); |
| 101 | let mut response = [0u8; HMAC_SIZE + NONCE_SIZE]; |
| 102 | response[..HMAC_SIZE].copy_from_slice(&worker_hmac); |
| 103 | response[HMAC_SIZE..].copy_from_slice(&worker_nonce); |
| 104 | stream.write_all(&response).await?; |
| 105 | stream.flush().await?; |
| 106 | |
| 107 | // Step 3: read master's HMAC response |
| 108 | let mut master_hmac = [0u8; HMAC_SIZE]; |
| 109 | stream.read_exact(&mut master_hmac).await?; |
| 110 | |
| 111 | // Step 4: verify master's HMAC |
| 112 | let expected = compute_hmac(key_bytes, &worker_nonce); |
| 113 | if !constant_time_eq(&master_hmac, &expected) { |
| 114 | return Err(anyhow!("master authentication failed: invalid HMAC")); |
| 115 | } |
| 116 | |
| 117 | Ok(()) |
| 118 | } |