use std::sync::Mutex;
use predicate_credential_system::{
cred::CredentialBase,
pcs::{HelperSecretKey, PredicateCredentialSystem},
};
use rand::{CryptoRng, RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use sha2::{Digest, Sha256};
use crate::{
VotingError,
election::{Election, check_community},
scheme::{Base, G1, Hvk, Open, VotingParams, key_text, open},
voter::TicketRequest,
};
pub const KEY_DERIVATION_INFO: &[u8] = b"vti-voting-pcs-keys/v1\0";
pub type PreTicket = <Base as CredentialBase>::PreCredential;
pub struct Registrar {
community: String,
open: Open,
hvk: Hvk,
hsk: HelperSecretKey<Base>,
hsk_lock: Mutex<()>,
}
impl std::fmt::Debug for Registrar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Registrar")
.field("community", &self.community)
.finish_non_exhaustive()
}
}
impl Registrar {
pub fn derive(community: &str, secret: &[u8; 32]) -> Result<Self, VotingError> {
check_community(community)?;
let mut h = Sha256::new();
h.update(KEY_DERIVATION_INFO);
h.update((community.len() as u64).to_le_bytes());
h.update(community.as_bytes());
h.update(secret);
let seed: [u8; 32] = h.finalize().into();
Self::generate(community, &mut ChaCha20Rng::from_seed(seed))
}
pub fn generate<R: RngCore + CryptoRng>(
community: &str,
rng: &mut R,
) -> Result<Self, VotingError> {
check_community(community)?;
let open = open(community)?;
let (hvk, hsk) = open.helper_keygen(rng);
Ok(Self {
community: community.to_string(),
open,
hvk,
hsk,
hsk_lock: Mutex::new(()),
})
}
pub fn community(&self) -> &str {
&self.community
}
pub fn hvk(&self) -> &Hvk {
&self.hvk
}
pub fn params(&self) -> VotingParams {
VotingParams::new(&self.community, self.hvk.clone())
.expect("the community was checked at construction")
}
pub fn public_text(&self) -> Result<String, VotingError> {
key_text(&self.hvk).map_err(|e| VotingError::Serialization(e.to_string()))
}
pub fn issue_ticket<R: RngCore + CryptoRng>(
&self,
election: &Election,
id: &G1,
request: &TicketRequest,
rng: &mut R,
) -> Result<PreTicket, VotingError> {
if election.community() != self.community {
return Err(VotingError::WrongCommunity {
expected: self.community.clone(),
actual: election.community().to_string(),
});
}
let f = election.predicate();
let _guard = self.hsk_lock.lock().expect("helper signing lock");
Ok(self
.open
.issue_root(&self.hvk, &self.hsk, &f, id, request, rng)?)
}
}