solana_zk_sdk/encryption/pod/
elgamal.rs1#[cfg(not(target_arch = "wasm32"))]
4use bytemuck::Zeroable;
5#[cfg(target_arch = "wasm32")]
6use wasm_bindgen::prelude::*;
7#[cfg(not(target_os = "solana"))]
8use {
9 crate::{
10 encryption::elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey},
11 errors::ElGamalError,
12 },
13 curve25519_dalek::ristretto::CompressedRistretto,
14};
15use {
16 crate::{
17 encryption::{DECRYPT_HANDLE_LEN, ELGAMAL_CIPHERTEXT_LEN, ELGAMAL_PUBKEY_LEN},
18 pod::{impl_from_bytes, impl_from_str, impl_wasm_bindings},
19 },
20 base64::{prelude::BASE64_STANDARD, Engine},
21 std::fmt,
22};
23
24const ELGAMAL_PUBKEY_MAX_BASE64_LEN: usize = 44;
26
27const ELGAMAL_CIPHERTEXT_MAX_BASE64_LEN: usize = 88;
29
30const DECRYPT_HANDLE_MAX_BASE64_LEN: usize = 44;
32
33#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
35#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)]
36#[repr(transparent)]
37pub struct PodElGamalCiphertext(pub(crate) [u8; ELGAMAL_CIPHERTEXT_LEN]);
38
39impl_wasm_bindings!(
40 POD_TYPE = PodElGamalCiphertext,
41 DECODED_TYPE = ElGamalCiphertext
42);
43
44impl fmt::Debug for PodElGamalCiphertext {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 write!(f, "{:?}", self.0)
47 }
48}
49
50impl fmt::Display for PodElGamalCiphertext {
51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 write!(f, "{}", BASE64_STANDARD.encode(self.0))
53 }
54}
55
56impl Default for PodElGamalCiphertext {
57 fn default() -> Self {
58 Self::zeroed()
59 }
60}
61
62impl_from_str!(
63 TYPE = PodElGamalCiphertext,
64 BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN,
65 BASE64_LEN = ELGAMAL_CIPHERTEXT_MAX_BASE64_LEN
66);
67
68impl_from_bytes!(
69 TYPE = PodElGamalCiphertext,
70 BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN
71);
72
73#[cfg(not(target_os = "solana"))]
74impl From<ElGamalCiphertext> for PodElGamalCiphertext {
75 fn from(decoded_ciphertext: ElGamalCiphertext) -> Self {
76 Self(decoded_ciphertext.to_bytes())
77 }
78}
79
80#[cfg(not(target_os = "solana"))]
81impl TryFrom<PodElGamalCiphertext> for ElGamalCiphertext {
82 type Error = ElGamalError;
83
84 fn try_from(pod_ciphertext: PodElGamalCiphertext) -> Result<Self, Self::Error> {
85 Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization)
86 }
87}
88
89#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
91#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)]
92#[repr(transparent)]
93pub struct PodElGamalPubkey(pub(crate) [u8; ELGAMAL_PUBKEY_LEN]);
94
95impl_wasm_bindings!(POD_TYPE = PodElGamalPubkey, DECODED_TYPE = ElGamalPubkey);
96
97impl fmt::Debug for PodElGamalPubkey {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 write!(f, "{:?}", self.0)
100 }
101}
102
103impl fmt::Display for PodElGamalPubkey {
104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 write!(f, "{}", BASE64_STANDARD.encode(self.0))
106 }
107}
108
109impl_from_str!(
110 TYPE = PodElGamalPubkey,
111 BYTES_LEN = ELGAMAL_PUBKEY_LEN,
112 BASE64_LEN = ELGAMAL_PUBKEY_MAX_BASE64_LEN
113);
114
115impl_from_bytes!(TYPE = PodElGamalPubkey, BYTES_LEN = ELGAMAL_PUBKEY_LEN);
116
117#[cfg(not(target_os = "solana"))]
118impl From<ElGamalPubkey> for PodElGamalPubkey {
119 fn from(decoded_pubkey: ElGamalPubkey) -> Self {
120 Self(decoded_pubkey.into())
121 }
122}
123
124#[cfg(not(target_os = "solana"))]
125impl TryFrom<PodElGamalPubkey> for ElGamalPubkey {
126 type Error = ElGamalError;
127
128 fn try_from(pod_pubkey: PodElGamalPubkey) -> Result<Self, Self::Error> {
129 Self::try_from(pod_pubkey.0.as_slice())
130 }
131}
132
133#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)]
135#[repr(transparent)]
136pub struct PodDecryptHandle(pub(crate) [u8; DECRYPT_HANDLE_LEN]);
137
138impl fmt::Debug for PodDecryptHandle {
139 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140 write!(f, "{:?}", self.0)
141 }
142}
143
144#[cfg(not(target_os = "solana"))]
145impl From<DecryptHandle> for PodDecryptHandle {
146 fn from(decoded_handle: DecryptHandle) -> Self {
147 Self(decoded_handle.to_bytes())
148 }
149}
150
151#[cfg(not(target_os = "solana"))]
153impl From<PodDecryptHandle> for CompressedRistretto {
154 fn from(pod_handle: PodDecryptHandle) -> Self {
155 Self(pod_handle.0)
156 }
157}
158
159#[cfg(not(target_os = "solana"))]
160impl TryFrom<PodDecryptHandle> for DecryptHandle {
161 type Error = ElGamalError;
162
163 fn try_from(pod_handle: PodDecryptHandle) -> Result<Self, Self::Error> {
164 Self::from_bytes(&pod_handle.0).ok_or(ElGamalError::CiphertextDeserialization)
165 }
166}
167
168impl fmt::Display for PodDecryptHandle {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 write!(f, "{}", BASE64_STANDARD.encode(self.0))
171 }
172}
173
174impl_from_str!(
175 TYPE = PodDecryptHandle,
176 BYTES_LEN = DECRYPT_HANDLE_LEN,
177 BASE64_LEN = DECRYPT_HANDLE_MAX_BASE64_LEN
178);
179
180impl_from_bytes!(TYPE = PodDecryptHandle, BYTES_LEN = DECRYPT_HANDLE_LEN);
181
182#[cfg(test)]
183mod tests {
184 use {super::*, crate::encryption::elgamal::ElGamalKeypair, std::str::FromStr};
185
186 #[test]
187 fn elgamal_pubkey_fromstr() {
188 let elgamal_keypair = ElGamalKeypair::new_rand();
189 let expected_elgamal_pubkey: PodElGamalPubkey = (*elgamal_keypair.pubkey()).into();
190
191 let elgamal_pubkey_base64_str = format!("{}", expected_elgamal_pubkey);
192 let computed_elgamal_pubkey =
193 PodElGamalPubkey::from_str(&elgamal_pubkey_base64_str).unwrap();
194
195 assert_eq!(expected_elgamal_pubkey, computed_elgamal_pubkey);
196 }
197
198 #[test]
199 fn elgamal_ciphertext_fromstr() {
200 let elgamal_keypair = ElGamalKeypair::new_rand();
201 let expected_elgamal_ciphertext: PodElGamalCiphertext =
202 elgamal_keypair.pubkey().encrypt(0_u64).into();
203
204 let elgamal_ciphertext_base64_str = format!("{}", expected_elgamal_ciphertext);
205 let computed_elgamal_ciphertext =
206 PodElGamalCiphertext::from_str(&elgamal_ciphertext_base64_str).unwrap();
207
208 assert_eq!(expected_elgamal_ciphertext, computed_elgamal_ciphertext);
209 }
210}