distributed_topic_tracker/crypto/
keys.rs

1use std::sync::Arc;
2
3use sha2::Digest;
4
5use crate::crypto::RecordTopic;
6
7
8pub trait SecretRotation: Send + Sync {
9    fn derive(
10        &self,
11        topic_hash: [u8; 32],
12        unix_minute: u64,
13        initial_secret_hash: [u8; 32],
14    ) -> [u8; 32];
15}
16
17#[derive(Debug, Clone)]
18pub struct DefaultSecretRotation;
19impl SecretRotation for DefaultSecretRotation {
20    fn derive(
21        &self,
22        topic_hash: [u8; 32],
23        unix_minute: u64,
24        initial_secret_hash: [u8; 32],
25    ) -> [u8; 32] {
26        use sha2::Digest;
27        let mut h = sha2::Sha512::new();
28        h.update(topic_hash);
29        h.update(unix_minute.to_be_bytes());
30        h.update(initial_secret_hash);
31        h.finalize()[..32].try_into().unwrap()
32    }
33}
34
35#[derive(Clone)]
36pub struct RotationHandle(Arc<dyn SecretRotation>);
37impl core::fmt::Debug for RotationHandle {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        f.debug_struct("RotationHandle").finish()
40    }
41}
42impl Default for RotationHandle {
43    fn default() -> Self {
44        Self(Arc::new(DefaultSecretRotation))
45    }
46}
47impl RotationHandle {
48    pub fn new(rotation: impl SecretRotation + 'static) -> Self {
49        Self(Arc::new(rotation))
50    }
51
52    pub fn derive(&self, topic_hash: [u8; 32], unix_minute: u64, initial_secret_hash: [u8; 32]) -> [u8; 32] {
53        self.0.derive(topic_hash, unix_minute, initial_secret_hash)
54    }
55}
56
57pub fn signing_keypair(record_topic: RecordTopic, unix_minute: u64) -> ed25519_dalek::SigningKey {
58    let mut sign_keypair_hash = sha2::Sha512::new();
59    sign_keypair_hash.update(record_topic.hash());
60    sign_keypair_hash.update(unix_minute.to_le_bytes());
61    let sign_keypair_seed: [u8; 32] = sign_keypair_hash.finalize()[..32]
62        .try_into()
63        .expect("hashing failed");
64    ed25519_dalek::SigningKey::from_bytes(&sign_keypair_seed)
65}
66
67pub fn encryption_keypair(
68    record_topic: RecordTopic,
69    secret_rotation_function: &RotationHandle,
70    initial_secret_hash: [u8; 32],
71    unix_minute: u64,
72) -> ed25519_dalek::SigningKey {
73    let enc_keypair_seed =
74        secret_rotation_function
75            .0
76            .derive(record_topic.hash(), unix_minute, initial_secret_hash);
77    ed25519_dalek::SigningKey::from_bytes(&enc_keypair_seed)
78}
79
80// salt = hash (topic + unix_minute)
81pub fn salt(record_topic: RecordTopic, unix_minute: u64) -> [u8; 32] {
82    let mut slot_hash = sha2::Sha512::new();
83    slot_hash.update(record_topic.hash());
84    slot_hash.update(unix_minute.to_le_bytes());
85    slot_hash.finalize()[..32]
86        .try_into()
87        .expect("hashing failed")
88}