1use crate::keys::{IncomingViewingKey, OutgoingViewingKey};
2use anyhow::{anyhow, Result};
3use chacha20poly1305::{
4 aead::{Aead, NewAead},
5 ChaCha20Poly1305, Key, Nonce,
6};
7use decaf377_ka as ka;
8use penumbra_sdk_asset::balance;
9use penumbra_sdk_proto::core::keys::v1::{self as pb};
10use penumbra_sdk_tct::StateCommitment;
11use rand::{CryptoRng, RngCore};
12
13pub const PAYLOAD_KEY_LEN_BYTES: usize = 32;
14pub const OVK_WRAPPED_LEN_BYTES: usize = 48;
15pub const MEMOKEY_WRAPPED_LEN_BYTES: usize = 48;
16
17pub enum PayloadKind {
19 Note,
21 MemoKey,
23 Memo,
25 Swap,
27}
28
29impl PayloadKind {
30 pub(crate) fn nonce(&self) -> [u8; 12] {
31 match self {
32 Self::Note => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
33 Self::MemoKey => [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
34 Self::Swap => [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
35 Self::Memo => [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
36 }
37 }
38}
39
40#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44pub struct PayloadKey(Key);
45
46impl PayloadKey {
47 pub fn derive(shared_secret: &ka::SharedSecret, epk: &ka::Public) -> Self {
49 let mut kdf_params = blake2b_simd::Params::new();
50 kdf_params.personal(b"Penumbra_Payload");
51 kdf_params.hash_length(32);
52 let mut kdf = kdf_params.to_state();
53 kdf.update(&shared_secret.0);
54 kdf.update(&epk.0);
55
56 let key = kdf.finalize();
57 Self(*Key::from_slice(key.as_bytes()))
58 }
59
60 pub fn random_key<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
62 let mut key_bytes = [0u8; 32];
63 rng.fill_bytes(&mut key_bytes);
64 Self(*Key::from_slice(&key_bytes[..]))
65 }
66
67 pub fn to_vec(&self) -> Vec<u8> {
68 self.0.to_vec()
69 }
70
71 pub fn encrypt(&self, plaintext: Vec<u8>, kind: PayloadKind) -> Vec<u8> {
73 let cipher = ChaCha20Poly1305::new(&self.0);
74 let nonce_bytes = kind.nonce();
75 let nonce = Nonce::from_slice(&nonce_bytes);
76
77 cipher
78 .encrypt(nonce, plaintext.as_ref())
79 .expect("encryption succeeded")
80 }
81
82 pub fn decrypt(&self, ciphertext: Vec<u8>, kind: PayloadKind) -> Result<Vec<u8>> {
84 let cipher = ChaCha20Poly1305::new(&self.0);
85
86 let nonce_bytes = kind.nonce();
87 let nonce = Nonce::from_slice(&nonce_bytes);
88
89 cipher
90 .decrypt(nonce, ciphertext.as_ref())
91 .map_err(|_| anyhow::anyhow!("decryption error"))
92 }
93
94 pub fn derive_swap(ovk: &OutgoingViewingKey, cm: StateCommitment) -> Self {
96 let cm_bytes: [u8; 32] = cm.into();
97
98 let mut kdf_params = blake2b_simd::Params::new();
99 kdf_params.personal(b"Penumbra_Payswap");
100 kdf_params.hash_length(32);
101 let mut kdf = kdf_params.to_state();
102 kdf.update(&ovk.to_bytes());
103 kdf.update(&cm_bytes);
104
105 let key = kdf.finalize();
106 Self(*Key::from_slice(key.as_bytes()))
107 }
108
109 pub fn encrypt_swap(&self, plaintext: Vec<u8>) -> Vec<u8> {
111 let cipher = ChaCha20Poly1305::new(&self.0);
112 let nonce_bytes = PayloadKind::Swap.nonce();
113 let nonce = Nonce::from_slice(&nonce_bytes);
114
115 cipher
116 .encrypt(nonce, plaintext.as_ref())
117 .expect("encryption succeeded")
118 }
119
120 pub fn decrypt_swap(&self, ciphertext: Vec<u8>) -> Result<Vec<u8>> {
122 let cipher = ChaCha20Poly1305::new(&self.0);
123
124 let nonce_bytes = PayloadKind::Swap.nonce();
125 let nonce = Nonce::from_slice(&nonce_bytes);
126
127 cipher
128 .decrypt(nonce, ciphertext.as_ref())
129 .map_err(|_| anyhow::anyhow!("decryption error"))
130 }
131}
132
133impl TryFrom<&[u8]> for PayloadKey {
134 type Error = anyhow::Error;
135
136 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
137 let bytes: [u8; PAYLOAD_KEY_LEN_BYTES] = slice
138 .try_into()
139 .map_err(|_| anyhow::anyhow!("PayloadKey incorrect len"))?;
140 Ok(Self(*Key::from_slice(&bytes)))
141 }
142}
143
144impl TryFrom<Vec<u8>> for PayloadKey {
145 type Error = anyhow::Error;
146
147 fn try_from(vector: Vec<u8>) -> Result<Self, Self::Error> {
148 vector.as_slice().try_into()
149 }
150}
151
152impl From<[u8; 32]> for PayloadKey {
153 fn from(bytes: [u8; 32]) -> Self {
154 Self(*Key::from_slice(&bytes))
155 }
156}
157
158impl TryFrom<pb::PayloadKey> for PayloadKey {
159 type Error = anyhow::Error;
160 fn try_from(msg: pb::PayloadKey) -> Result<Self, Self::Error> {
161 msg.inner.as_slice().try_into()
162 }
163}
164
165impl From<PayloadKey> for pb::PayloadKey {
166 fn from(msg: PayloadKey) -> Self {
167 pb::PayloadKey {
168 inner: msg.0.to_vec(),
169 }
170 }
171}
172
173pub struct OutgoingCipherKey(Key);
178
179impl OutgoingCipherKey {
180 pub fn derive(
182 ovk: &OutgoingViewingKey,
183 cv: balance::Commitment,
184 cm: StateCommitment,
185 epk: &ka::Public,
186 ) -> Self {
187 let cv_bytes: [u8; 32] = cv.into();
188 let cm_bytes: [u8; 32] = cm.into();
189
190 let mut kdf_params = blake2b_simd::Params::new();
191 kdf_params.hash_length(32);
192 kdf_params.personal(b"Penumbra_OutCiph");
193 let mut kdf = kdf_params.to_state();
194 kdf.update(&ovk.to_bytes());
195 kdf.update(&cv_bytes);
196 kdf.update(&cm_bytes);
197 kdf.update(&epk.0);
198
199 let key = kdf.finalize();
200 Self(*Key::from_slice(key.as_bytes()))
201 }
202
203 pub fn encrypt(&self, plaintext: Vec<u8>, kind: PayloadKind) -> Vec<u8> {
205 let cipher = ChaCha20Poly1305::new(&self.0);
206
207 let nonce_bytes = kind.nonce();
217 let nonce = Nonce::from_slice(&nonce_bytes);
218
219 cipher
220 .encrypt(nonce, plaintext.as_ref())
221 .expect("encryption succeeded")
222 }
223
224 pub fn decrypt(&self, ciphertext: Vec<u8>, kind: PayloadKind) -> Result<Vec<u8>> {
226 let cipher = ChaCha20Poly1305::new(&self.0);
227 let nonce_bytes = kind.nonce();
228 let nonce = Nonce::from_slice(&nonce_bytes);
229
230 cipher
231 .decrypt(nonce, ciphertext.as_ref())
232 .map_err(|_| anyhow::anyhow!("decryption error"))
233 }
234}
235
236#[derive(Clone, Debug)]
238pub struct OvkWrappedKey(pub [u8; OVK_WRAPPED_LEN_BYTES]);
239
240impl OvkWrappedKey {
241 pub fn to_vec(&self) -> Vec<u8> {
242 self.0.to_vec()
243 }
244}
245
246impl TryFrom<Vec<u8>> for OvkWrappedKey {
247 type Error = anyhow::Error;
248
249 fn try_from(vector: Vec<u8>) -> Result<Self, Self::Error> {
250 let bytes: [u8; OVK_WRAPPED_LEN_BYTES] = vector
251 .try_into()
252 .map_err(|_| anyhow::anyhow!("wrapped OVK malformed"))?;
253 Ok(Self(bytes))
254 }
255}
256
257impl TryFrom<&[u8]> for OvkWrappedKey {
258 type Error = anyhow::Error;
259
260 fn try_from(arr: &[u8]) -> Result<Self, Self::Error> {
261 let bytes: [u8; OVK_WRAPPED_LEN_BYTES] = arr
262 .try_into()
263 .map_err(|_| anyhow::anyhow!("wrapped OVK malformed"))?;
264 Ok(Self(bytes))
265 }
266}
267
268#[derive(Clone, Debug)]
270pub struct WrappedMemoKey(pub [u8; MEMOKEY_WRAPPED_LEN_BYTES]);
271
272impl WrappedMemoKey {
273 pub fn to_vec(&self) -> Vec<u8> {
274 self.0.to_vec()
275 }
276
277 pub fn encrypt(
279 memo_key: &PayloadKey,
280 esk: ka::Secret,
281 transmission_key: &ka::Public,
282 diversified_generator: &decaf377::Element,
283 ) -> Self {
284 let epk = esk.diversified_public(diversified_generator);
286 let shared_secret = esk
287 .key_agreement_with(transmission_key)
288 .expect("key agreement succeeded");
289
290 let action_key = PayloadKey::derive(&shared_secret, &epk);
291 let encrypted_memo_key = action_key.encrypt(memo_key.to_vec(), PayloadKind::MemoKey);
293 let wrapped_memo_key_bytes: [u8; MEMOKEY_WRAPPED_LEN_BYTES] = encrypted_memo_key
294 .try_into()
295 .expect("memo key must fit in wrapped memo key field");
296
297 WrappedMemoKey(wrapped_memo_key_bytes)
298 }
299
300 pub fn decrypt(&self, epk: ka::Public, ivk: &IncomingViewingKey) -> Result<PayloadKey> {
302 let shared_secret = ivk
304 .key_agreement_with(&epk)
305 .expect("key agreement succeeded");
306
307 let action_key = PayloadKey::derive(&shared_secret, &epk);
308 let decrypted_memo_key = action_key
310 .decrypt(self.to_vec(), PayloadKind::MemoKey)
311 .map_err(|_| anyhow!("decryption error"))?;
312
313 decrypted_memo_key.try_into()
314 }
315
316 pub fn decrypt_outgoing(&self, action_key: &PayloadKey) -> Result<PayloadKey> {
318 let decrypted_memo_key = action_key
319 .decrypt(self.to_vec(), PayloadKind::MemoKey)
320 .map_err(|_| anyhow!("decryption error"))?;
321 decrypted_memo_key.try_into()
322 }
323}
324
325impl TryFrom<Vec<u8>> for WrappedMemoKey {
326 type Error = anyhow::Error;
327
328 fn try_from(vector: Vec<u8>) -> Result<Self, Self::Error> {
329 let bytes: [u8; MEMOKEY_WRAPPED_LEN_BYTES] = vector
330 .try_into()
331 .map_err(|_| anyhow::anyhow!("wrapped memo key malformed"))?;
332 Ok(Self(bytes))
333 }
334}
335
336impl TryFrom<&[u8]> for WrappedMemoKey {
337 type Error = anyhow::Error;
338
339 fn try_from(arr: &[u8]) -> Result<Self, Self::Error> {
340 let bytes: [u8; MEMOKEY_WRAPPED_LEN_BYTES] = arr
341 .try_into()
342 .map_err(|_| anyhow::anyhow!("wrapped memo key malformed"))?;
343 Ok(Self(bytes))
344 }
345}
346
347#[derive(Debug, Copy, Clone, PartialEq, Eq)]
349pub struct BackreferenceKey(pub Key);
350
351impl BackreferenceKey {
352 pub fn derive(ovk: &OutgoingViewingKey) -> Self {
353 let mut kdf_params = blake2b_simd::Params::new();
354 kdf_params.personal(b"Penumbra_Backref");
355 kdf_params.hash_length(32);
356 let mut kdf = kdf_params.to_state();
357 kdf.update(&ovk.to_bytes());
358
359 let key = kdf.finalize();
360 Self(*Key::from_slice(key.as_bytes()))
361 }
362}