use std::io::{self, Read, Write};
use melin_transport_core::replication::protocol::{
MAX_CONTROL_FRAME, decode_auth_result, decode_challenge, decode_challenge_response,
encode_auth_failed, encode_auth_ok, encode_challenge, encode_challenge_response, read_frame,
};
pub(super) fn authenticate_replica(
reader: &mut impl Read,
writer: &mut impl Write,
authorized_keys: &melin_app::auth::AuthorizedKeys,
) -> io::Result<()> {
use ed25519_dalek::{Verifier, VerifyingKey};
let mut nonce = [0u8; 32];
getrandom::fill(&mut nonce).map_err(|e| io::Error::other(format!("getrandom failed: {e}")))?;
let mut buf = Vec::with_capacity(64);
encode_challenge(&nonce, &mut buf);
writer.write_all(&buf)?;
writer.flush()?;
let frame = read_frame(reader, MAX_CONTROL_FRAME)?;
let (signature_bytes, pubkey_bytes) = match decode_challenge_response(&frame) {
Ok(pair) => pair,
Err(e) => {
buf.clear();
encode_auth_failed(&mut buf);
let _ = writer.write_all(&buf);
return Err(io::Error::other(format!("bad challenge response: {e}")));
}
};
let permission = match authorized_keys.lookup(&pubkey_bytes) {
Some(perm) => perm,
None => {
buf.clear();
encode_auth_failed(&mut buf);
let _ = writer.write_all(&buf);
return Err(io::Error::other("unknown replication key"));
}
};
if !permission.is_replication() {
buf.clear();
encode_auth_failed(&mut buf);
let _ = writer.write_all(&buf);
return Err(io::Error::other(format!(
"key has {permission:?} permission, expected Replication"
)));
}
let verifying_key = VerifyingKey::from_bytes(&pubkey_bytes).map_err(|e| {
buf.clear();
encode_auth_failed(&mut buf);
let _ = writer.write_all(&buf);
io::Error::other(format!("invalid public key: {e}"))
})?;
let signature = ed25519_dalek::Signature::from_bytes(&signature_bytes);
verifying_key.verify(&nonce, &signature).map_err(|e| {
buf.clear();
encode_auth_failed(&mut buf);
let _ = writer.write_all(&buf);
io::Error::other(format!("signature verification failed: {e}"))
})?;
buf.clear();
encode_auth_ok(&mut buf);
writer.write_all(&buf)?;
writer.flush()?;
Ok(())
}
pub(super) fn authenticate_with_primary(
reader: &mut impl Read,
writer: &mut impl Write,
signing_key: &ed25519_dalek::SigningKey,
) -> io::Result<()> {
use ed25519_dalek::Signer;
let frame = read_frame(reader, MAX_CONTROL_FRAME)?;
let nonce = decode_challenge(&frame)?;
let signature = signing_key.sign(&nonce);
let pubkey = signing_key.verifying_key();
let mut buf = Vec::with_capacity(128);
encode_challenge_response(&signature.to_bytes(), pubkey.as_bytes(), &mut buf);
writer.write_all(&buf)?;
writer.flush()?;
let result_frame = read_frame(reader, MAX_CONTROL_FRAME)?;
match decode_auth_result(&result_frame)? {
true => Ok(()),
false => Err(io::Error::other("primary rejected replication key")),
}
}