distributed_topic_tracker/crypto/
keys.rs1use std::sync::Arc;
2
3use sha2::Digest;
4
5use crate::crypto::RecordTopic;
6
7pub trait SecretRotation: Send + Sync {
12 fn derive(
24 &self,
25 topic_hash: [u8; 32],
26 unix_minute: u64,
27 initial_secret_hash: [u8; 32],
28 ) -> [u8; 32];
29}
30
31#[derive(Debug, Clone)]
35pub struct DefaultSecretRotation;
36
37impl SecretRotation for DefaultSecretRotation {
38 fn derive(
39 &self,
40 topic_hash: [u8; 32],
41 unix_minute: u64,
42 initial_secret_hash: [u8; 32],
43 ) -> [u8; 32] {
44 use sha2::Digest;
45 let mut h = sha2::Sha512::new();
46 h.update(topic_hash);
47 h.update(unix_minute.to_be_bytes());
48 h.update(initial_secret_hash);
49 h.finalize()[..32].try_into().unwrap()
50 }
51}
52
53#[derive(Clone)]
57pub struct RotationHandle(Arc<dyn SecretRotation>);
58
59impl core::fmt::Debug for RotationHandle {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("RotationHandle").finish()
62 }
63}
64
65impl Default for RotationHandle {
66 fn default() -> Self {
67 Self(Arc::new(DefaultSecretRotation))
68 }
69}
70
71impl RotationHandle {
72 pub fn new(rotation: impl SecretRotation + 'static) -> Self {
74 Self(Arc::new(rotation))
75 }
76
77 pub fn derive(
79 &self,
80 topic_hash: [u8; 32],
81 unix_minute: u64,
82 initial_secret_hash: [u8; 32],
83 ) -> [u8; 32] {
84 self.0.derive(topic_hash, unix_minute, initial_secret_hash)
85 }
86}
87
88pub fn signing_keypair(record_topic: RecordTopic, unix_minute: u64) -> ed25519_dalek::SigningKey {
104 let mut sign_keypair_hash = sha2::Sha512::new();
105 sign_keypair_hash.update(record_topic.hash());
106 sign_keypair_hash.update(unix_minute.to_le_bytes());
107 let sign_keypair_seed: [u8; 32] = sign_keypair_hash.finalize()[..32]
108 .try_into()
109 .expect("hashing failed");
110 ed25519_dalek::SigningKey::from_bytes(&sign_keypair_seed)
111}
112
113pub fn encryption_keypair(
125 record_topic: RecordTopic,
126 secret_rotation_function: &RotationHandle,
127 initial_secret_hash: [u8; 32],
128 unix_minute: u64,
129) -> ed25519_dalek::SigningKey {
130 let enc_keypair_seed =
131 secret_rotation_function
132 .0
133 .derive(record_topic.hash(), unix_minute, initial_secret_hash);
134 ed25519_dalek::SigningKey::from_bytes(&enc_keypair_seed)
135}
136
137pub fn salt(record_topic: RecordTopic, unix_minute: u64) -> [u8; 32] {
142 let mut slot_hash = sha2::Sha512::new();
143 slot_hash.update(record_topic.hash());
144 slot_hash.update(unix_minute.to_le_bytes());
145 slot_hash.finalize()[..32]
146 .try_into()
147 .expect("hashing failed")
148}