p2panda_encryption/crypto/
x25519.rs1use std::fmt;
5use std::hash::Hash as StdHash;
6
7use curve25519_dalek::scalar::clamp_integer;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use crate::crypto::Secret;
12use crate::{Rng, RngError};
13
14pub const SECRET_KEY_SIZE: usize = 32;
16
17pub const PUBLIC_KEY_SIZE: usize = 32;
19
20pub const SHARED_SECRET_SIZE: usize = 32;
22
23#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
25pub struct SecretKey(Secret<SECRET_KEY_SIZE>);
26
27impl SecretKey {
28 #[cfg(not(feature = "test_utils"))]
29 pub(crate) fn from_bytes(bytes: [u8; SECRET_KEY_SIZE]) -> Self {
30 SecretKey(Secret::from_bytes(clamp_integer(bytes)))
31 }
32
33 #[cfg(feature = "test_utils")]
34 pub fn from_bytes(bytes: [u8; SECRET_KEY_SIZE]) -> Self {
35 SecretKey(Secret::from_bytes(clamp_integer(bytes)))
36 }
37
38 pub fn from_rng(rng: &Rng) -> Result<Self, RngError> {
39 Ok(Self::from_bytes(rng.random_array()?))
40 }
41
42 pub(crate) fn as_bytes(&self) -> &[u8; SECRET_KEY_SIZE] {
43 self.0.as_bytes()
44 }
45
46 pub fn public_key(&self) -> Result<PublicKey, X25519Error> {
47 let static_secret = x25519_dalek::StaticSecret::from(*self.0.as_bytes());
48 let public_key = x25519_dalek::PublicKey::from(&static_secret);
49 Ok(PublicKey(public_key.to_bytes()))
50 }
51
52 pub fn calculate_agreement(
53 &self,
54 their_public: &PublicKey,
55 ) -> Result<[u8; SHARED_SECRET_SIZE], X25519Error> {
56 let static_secret = x25519_dalek::StaticSecret::from(*self.0.as_bytes());
57 let shared_secret =
58 static_secret.diffie_hellman(&x25519_dalek::PublicKey::from(their_public.to_bytes()));
59 Ok(shared_secret.to_bytes())
60 }
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq, StdHash, Serialize, Deserialize)]
65pub struct PublicKey(#[serde(with = "serde_bytes")] [u8; PUBLIC_KEY_SIZE]);
66
67impl PublicKey {
68 pub fn from_bytes(public_key: [u8; PUBLIC_KEY_SIZE]) -> Self {
69 Self(public_key)
70 }
71
72 pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
73 &self.0
74 }
75
76 pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
77 self.0
78 }
79
80 pub fn to_hex(self) -> String {
81 hex::encode(self.as_bytes())
82 }
83}
84
85impl fmt::Display for PublicKey {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "{}", self.to_hex())
88 }
89}
90
91#[derive(Debug, Error)]
92pub enum X25519Error {
93 #[error("invalid curve point or scalar")]
94 InvalidCurve,
95}
96
97#[cfg(test)]
98mod tests {
99 use crate::crypto::Rng;
100
101 use super::SecretKey;
102
103 #[test]
104 fn diffie_hellmann() {
105 let rng = Rng::from_seed([1; 32]);
106
107 let alice_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
108 let alice_public_key = alice_secret_key.public_key().unwrap();
109
110 let bob_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
111 let bob_public_key = bob_secret_key.public_key().unwrap();
112
113 let alice_shared_secret = alice_secret_key
114 .calculate_agreement(&bob_public_key)
115 .unwrap();
116 let bob_shared_secret = bob_secret_key
117 .calculate_agreement(&alice_public_key)
118 .unwrap();
119
120 assert_eq!(alice_shared_secret, bob_shared_secret);
121 }
122}