use crate::constants::*;
use crate::error::{Error, Result};
use crate::keys::{KeyPair, PublicKeyBundle};
use crate::sign;
use crate::types::HybridSignature;
use crate::wire::{read_header, write_header};
use alloc::vec::Vec;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RotationAttestation {
epoch: u64,
new_public: PublicKeyBundle,
signature: HybridSignature,
}
impl RotationAttestation {
pub fn new_public(&self) -> &PublicKeyBundle {
&self.new_public
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(HEADER_LEN + 8 + PUBLIC_BUNDLE_LEN + SIGNATURE_LEN);
write_header(&mut out, MAGIC_ROTATION);
out.extend_from_slice(&self.epoch.to_be_bytes());
out.extend_from_slice(&self.new_public.to_bytes());
out.extend_from_slice(&self.signature.to_bytes());
out
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let rest = read_header(bytes, MAGIC_ROTATION, Error::InvalidSignature)?;
if rest.len() != 8 + PUBLIC_BUNDLE_LEN + SIGNATURE_LEN {
return Err(Error::InvalidSignature);
}
let (epoch_bytes, rest) = rest.split_at(8);
let epoch =
u64::from_be_bytes(epoch_bytes.try_into().expect("split_at guarantees 8 bytes"));
let (bundle_bytes, sig_bytes) = rest.split_at(PUBLIC_BUNDLE_LEN);
let new_public = PublicKeyBundle::from_bytes(bundle_bytes)?;
let signature = HybridSignature::from_bytes(sig_bytes)?;
Ok(Self {
epoch,
new_public,
signature,
})
}
}
fn rotation_message(old: &PublicKeyBundle, epoch: u64, new_public: &PublicKeyBundle) -> Vec<u8> {
let mut msg = Vec::with_capacity(KEY_ID_LEN + 8 + PUBLIC_BUNDLE_LEN);
msg.extend_from_slice(old.key_id().as_bytes());
msg.extend_from_slice(&epoch.to_be_bytes());
msg.extend_from_slice(&new_public.to_bytes());
msg
}
pub(crate) fn attest_rotation(
old: &KeyPair,
new_public: &PublicKeyBundle,
epoch: u64,
) -> Result<RotationAttestation> {
let message = rotation_message(old.public_keys(), epoch, new_public);
let signature = sign::sign(old, &message, ROTATION_CONTEXT)?;
Ok(RotationAttestation {
epoch,
new_public: new_public.clone(),
signature,
})
}
pub fn verify_rotation<'a>(
old: &PublicKeyBundle,
attestation: &'a RotationAttestation,
) -> Result<&'a PublicKeyBundle> {
let message = rotation_message(old, attestation.epoch, &attestation.new_public);
sign::verify(&message, ROTATION_CONTEXT, &attestation.signature, old)?;
Ok(&attestation.new_public)
}