use super::{CipherState, HandshakeRole, NoiseError, ReplayWindow};
use ring::aead::LessSafeKey;
use secp256k1::{PublicKey, XOnlyPublicKey};
use std::{
fmt,
ops::Range,
sync::{
Arc,
atomic::{AtomicU64, Ordering},
},
};
#[derive(Clone)]
pub(crate) struct SendCounterAuthority {
next: Arc<AtomicU64>,
}
impl SendCounterAuthority {
fn new(next: u64) -> Self {
Self {
next: Arc::new(AtomicU64::new(next)),
}
}
pub(crate) fn current(&self) -> u64 {
self.next.load(Ordering::Relaxed)
}
pub(crate) fn reserve(&self) -> Result<u64, NoiseError> {
self.next
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |next| {
if next == u64::MAX {
None
} else {
Some(next + 1)
}
})
.map_err(|_| NoiseError::NonceOverflow)
}
pub(crate) fn reserve_range(&self, count: usize) -> Result<Range<u64>, NoiseError> {
let count = u64::try_from(count).map_err(|_| NoiseError::NonceOverflow)?;
if count == 0 {
let current = self.current();
return Ok(current..current);
}
let first = self
.next
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |next| {
if next <= u64::MAX - count {
Some(next + count)
} else {
None
}
})
.map_err(|_| NoiseError::NonceOverflow)?;
Ok(first..first + count)
}
}
pub struct NoiseSession {
role: HandshakeRole,
send_cipher: CipherState,
send_counter: SendCounterAuthority,
recv_cipher: CipherState,
handshake_hash: [u8; 32],
remote_static: PublicKey,
replay_window: ReplayWindow,
}
impl NoiseSession {
pub(super) fn from_handshake(
role: HandshakeRole,
send_cipher: CipherState,
recv_cipher: CipherState,
handshake_hash: [u8; 32],
remote_static: PublicKey,
) -> Self {
let send_counter = SendCounterAuthority::new(send_cipher.nonce());
Self {
role,
send_cipher,
send_counter,
recv_cipher,
handshake_hash,
remote_static,
replay_window: ReplayWindow::new(),
}
}
pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, NoiseError> {
let counter = self.take_send_counter()?;
self.send_cipher.encrypt_with_counter(plaintext, counter)
}
pub fn current_send_counter(&self) -> u64 {
self.send_counter.current()
}
pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, NoiseError> {
self.recv_cipher.decrypt(ciphertext)
}
pub fn check_replay(&self, counter: u64) -> Result<(), NoiseError> {
if self.replay_window.check(counter) {
Ok(())
} else {
Err(NoiseError::ReplayDetected(counter))
}
}
pub fn decrypt_with_replay_check(
&mut self,
ciphertext: &[u8],
counter: u64,
) -> Result<Vec<u8>, NoiseError> {
if !self.replay_window.check(counter) {
return Err(NoiseError::ReplayDetected(counter));
}
let plaintext = self.recv_cipher.decrypt_with_counter(ciphertext, counter)?;
self.replay_window.accept(counter);
Ok(plaintext)
}
pub fn encrypt_with_aad(
&mut self,
plaintext: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, NoiseError> {
let counter = self.take_send_counter()?;
self.send_cipher
.encrypt_with_counter_and_aad(plaintext, counter, aad)
}
pub fn decrypt_with_replay_check_and_aad(
&mut self,
ciphertext: &[u8],
counter: u64,
aad: &[u8],
) -> Result<Vec<u8>, NoiseError> {
if !self.replay_window.check(counter) {
return Err(NoiseError::ReplayDetected(counter));
}
let plaintext = self
.recv_cipher
.decrypt_with_counter_and_aad(ciphertext, counter, aad)?;
self.replay_window.accept(counter);
Ok(plaintext)
}
pub fn decrypt_with_replay_check_and_aad_in_place(
&mut self,
buf: &mut [u8],
counter: u64,
aad: &[u8],
) -> Result<usize, NoiseError> {
if !self.replay_window.check(counter) {
return Err(NoiseError::ReplayDetected(counter));
}
let plaintext_len = self
.recv_cipher
.decrypt_with_counter_and_aad_in_place(buf, counter, aad)?;
self.replay_window.accept(counter);
Ok(plaintext_len)
}
pub fn highest_received_counter(&self) -> u64 {
self.replay_window.highest()
}
pub fn recv_cipher_clone(&self) -> Option<LessSafeKey> {
self.recv_cipher.cipher_clone()
}
pub fn recv_replay_snapshot_owned(&self) -> crate::noise::ReplayWindow {
self.replay_window.clone()
}
pub fn send_cipher_clone(&self) -> Option<LessSafeKey> {
self.send_cipher.cipher_clone()
}
pub(crate) fn send_counter_authority(&self) -> SendCounterAuthority {
self.send_counter.clone()
}
pub fn has_send_cipher(&self) -> bool {
self.send_cipher.has_key()
}
pub fn take_send_counter(&self) -> Result<u64, NoiseError> {
self.send_counter.reserve()
}
pub fn accept_replay(&mut self, counter: u64) {
self.replay_window.accept(counter);
}
pub fn reset_replay_window(&mut self) {
self.replay_window.reset();
}
pub fn handshake_hash(&self) -> &[u8; 32] {
&self.handshake_hash
}
pub fn remote_static(&self) -> &PublicKey {
&self.remote_static
}
pub fn remote_static_xonly(&self) -> XOnlyPublicKey {
self.remote_static.x_only_public_key().0
}
pub fn role(&self) -> HandshakeRole {
self.role
}
pub fn send_nonce(&self) -> u64 {
self.send_counter.current()
}
pub fn recv_nonce(&self) -> u64 {
self.recv_cipher.nonce()
}
}
impl fmt::Debug for NoiseSession {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NoiseSession")
.field("role", &self.role)
.field("send_nonce", &self.send_counter.current())
.field("recv_nonce", &self.recv_cipher.nonce())
.field("handshake_hash", &hex::encode(&self.handshake_hash[..8]))
.finish()
}
}