distributed_topic_tracker/crypto/
keys.rs1use std::sync::Arc;
2
3use sha2::Digest;
4
5use crate::topic::topic::TopicId;
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(topic_id: &TopicId, unix_minute: u64) -> ed25519_dalek::SigningKey {
58 let mut sign_keypair_hash = sha2::Sha512::new();
59 sign_keypair_hash.update(topic_id.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 topic_id: &TopicId,
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(topic_id.hash(), unix_minute, initial_secret_hash);
77 ed25519_dalek::SigningKey::from_bytes(&enc_keypair_seed)
78}
79
80pub fn salt(topic_id: &TopicId, unix_minute: u64) -> [u8; 32] {
82 let mut slot_hash = sha2::Sha512::new();
83 slot_hash.update(topic_id.hash());
84 slot_hash.update(unix_minute.to_le_bytes());
85 slot_hash.finalize()[..32]
86 .try_into()
87 .expect("hashing failed")
88}