ironfish_primitives/sapling/
keys.rs1use std::convert::TryInto;
8use std::io::{self, Read, Write};
9
10use crate::{
11 constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
12 keys::{prf_expand, OutgoingViewingKey},
13 zip32,
14};
15use ff::PrimeField;
16use group::{Group, GroupEncoding};
17use subtle::CtOption;
18
19use super::{PaymentAddress, ProofGenerationKey, SaplingIvk, ViewingKey};
20
21#[derive(Clone)]
23pub struct ExpandedSpendingKey {
24 pub ask: ironfish_jubjub::Fr,
25 pub nsk: ironfish_jubjub::Fr,
26 pub ovk: OutgoingViewingKey,
27}
28
29#[derive(Debug)]
31pub struct FullViewingKey {
32 pub vk: ViewingKey,
33 pub ovk: OutgoingViewingKey,
34}
35
36impl ExpandedSpendingKey {
37 pub fn from_spending_key(sk: &[u8]) -> Self {
38 let ask = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array());
39 let nsk = ironfish_jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array());
40 let mut ovk = OutgoingViewingKey([0u8; 32]);
41 ovk.0
42 .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]);
43 ExpandedSpendingKey { ask, nsk, ovk }
44 }
45
46 pub fn proof_generation_key(&self) -> ProofGenerationKey {
47 ProofGenerationKey {
48 ak: *SPENDING_KEY_GENERATOR * self.ask,
49 nsk: self.nsk,
50 }
51 }
52
53 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
54 let mut ask_repr = [0u8; 32];
55 reader.read_exact(ask_repr.as_mut())?;
56 let ask = Option::from(ironfish_jubjub::Fr::from_repr(ask_repr))
57 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?;
58
59 let mut nsk_repr = [0u8; 32];
60 reader.read_exact(nsk_repr.as_mut())?;
61 let nsk = Option::from(ironfish_jubjub::Fr::from_repr(nsk_repr))
62 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?;
63
64 let mut ovk = [0u8; 32];
65 reader.read_exact(&mut ovk)?;
66
67 Ok(ExpandedSpendingKey {
68 ask,
69 nsk,
70 ovk: OutgoingViewingKey(ovk),
71 })
72 }
73
74 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
75 writer.write_all(self.ask.to_repr().as_ref())?;
76 writer.write_all(self.nsk.to_repr().as_ref())?;
77 writer.write_all(&self.ovk.0)?;
78
79 Ok(())
80 }
81
82 pub fn to_bytes(&self) -> [u8; 96] {
83 let mut result = [0u8; 96];
84 self.write(&mut result[..])
85 .expect("should be able to serialize an ExpandedSpendingKey");
86 result
87 }
88}
89
90impl Clone for FullViewingKey {
91 fn clone(&self) -> Self {
92 FullViewingKey {
93 vk: ViewingKey {
94 ak: self.vk.ak,
95 nk: self.vk.nk,
96 },
97 ovk: self.ovk,
98 }
99 }
100}
101
102impl FullViewingKey {
103 pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
104 FullViewingKey {
105 vk: ViewingKey {
106 ak: *SPENDING_KEY_GENERATOR * expsk.ask,
107 nk: *PROOF_GENERATION_KEY_GENERATOR * expsk.nsk,
108 },
109 ovk: expsk.ovk,
110 }
111 }
112
113 pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
114 let ak = {
115 let mut buf = [0u8; 32];
116 reader.read_exact(&mut buf)?;
117 ironfish_jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity()))
118 };
119 let nk = {
120 let mut buf = [0u8; 32];
121 reader.read_exact(&mut buf)?;
122 ironfish_jubjub::SubgroupPoint::from_bytes(&buf)
123 };
124 if ak.is_none().into() {
125 return Err(io::Error::new(
126 io::ErrorKind::InvalidInput,
127 "ak not of prime order",
128 ));
129 }
130 if nk.is_none().into() {
131 return Err(io::Error::new(
132 io::ErrorKind::InvalidInput,
133 "nk not in prime-order subgroup",
134 ));
135 }
136 let ak = ak.unwrap();
137 let nk = nk.unwrap();
138
139 let mut ovk = [0u8; 32];
140 reader.read_exact(&mut ovk)?;
141
142 Ok(FullViewingKey {
143 vk: ViewingKey { ak, nk },
144 ovk: OutgoingViewingKey(ovk),
145 })
146 }
147
148 pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
149 writer.write_all(&self.vk.ak.to_bytes())?;
150 writer.write_all(&self.vk.nk.to_bytes())?;
151 writer.write_all(&self.ovk.0)?;
152
153 Ok(())
154 }
155
156 pub fn to_bytes(&self) -> [u8; 96] {
157 let mut result = [0u8; 96];
158 self.write(&mut result[..])
159 .expect("should be able to serialize a FullViewingKey");
160 result
161 }
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173pub enum Scope {
174 External,
177 Internal,
180}
181
182#[derive(Clone, Debug)]
192pub struct DiversifiableFullViewingKey {
193 fvk: FullViewingKey,
194 dk: zip32::DiversifierKey,
195}
196
197impl From<zip32::ExtendedFullViewingKey> for DiversifiableFullViewingKey {
198 fn from(extfvk: zip32::ExtendedFullViewingKey) -> Self {
199 Self {
200 fvk: extfvk.fvk,
201 dk: extfvk.dk,
202 }
203 }
204}
205
206impl DiversifiableFullViewingKey {
207 pub fn from_bytes(bytes: &[u8; 128]) -> Option<Self> {
212 FullViewingKey::read(&bytes[..96]).ok().map(|fvk| Self {
213 fvk,
214 dk: zip32::DiversifierKey(bytes[96..].try_into().unwrap()),
215 })
216 }
217
218 pub fn to_bytes(&self) -> [u8; 128] {
220 let mut bytes = [0; 128];
221 self.fvk
222 .write(&mut bytes[..96])
223 .expect("slice should be the correct length");
224 bytes[96..].copy_from_slice(&self.dk.0);
225 bytes
226 }
227
228 fn derive_internal(&self) -> Self {
231 let (fvk, dk) = zip32::sapling_derive_internal_fvk(&self.fvk, &self.dk);
232 Self { fvk, dk }
233 }
234
235 pub fn fvk(&self) -> &FullViewingKey {
237 &self.fvk
238 }
239
240 pub fn to_ivk(&self, scope: Scope) -> SaplingIvk {
242 match scope {
243 Scope::External => self.fvk.vk.ivk(),
244 Scope::Internal => self.derive_internal().fvk.vk.ivk(),
245 }
246 }
247
248 pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
250 match scope {
251 Scope::External => self.fvk.ovk,
252 Scope::Internal => self.derive_internal().fvk.ovk,
253 }
254 }
255
256 pub fn address(&self, j: zip32::DiversifierIndex) -> Option<PaymentAddress> {
261 zip32::sapling_address(&self.fvk, &self.dk, j)
262 }
263
264 pub fn find_address(
273 &self,
274 j: zip32::DiversifierIndex,
275 ) -> Option<(zip32::DiversifierIndex, PaymentAddress)> {
276 zip32::sapling_find_address(&self.fvk, &self.dk, j)
277 }
278
279 pub fn default_address(&self) -> (zip32::DiversifierIndex, PaymentAddress) {
283 zip32::sapling_default_address(&self.fvk, &self.dk)
284 }
285
286 pub fn decrypt_diversifier(
296 &self,
297 addr: &PaymentAddress,
298 ) -> Option<(zip32::DiversifierIndex, Scope)> {
299 let j_external = self.dk.diversifier_index(addr.diversifier());
300 if self.address(j_external).as_ref() == Some(addr) {
301 return Some((j_external, Scope::External));
302 }
303
304 let j_internal = self
305 .derive_internal()
306 .dk
307 .diversifier_index(addr.diversifier());
308 if self.address(j_internal).as_ref() == Some(addr) {
309 return Some((j_internal, Scope::Internal));
310 }
311
312 None
313 }
314}
315
316#[cfg(any(test, feature = "test-dependencies"))]
317pub mod testing {
318 use proptest::collection::vec;
319 use proptest::prelude::{any, prop_compose};
320
321 use crate::{
322 sapling::PaymentAddress,
323 zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
324 };
325
326 prop_compose! {
327 pub fn arb_extended_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExtendedSpendingKey {
328 ExtendedSpendingKey::master(&v)
329 }
330 }
331
332 prop_compose! {
333 pub fn arb_shielded_addr()(extsk in arb_extended_spending_key()) -> PaymentAddress {
334 let extfvk = ExtendedFullViewingKey::from(&extsk);
335 extfvk.default_address().1
336 }
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use group::{Group, GroupEncoding};
343
344 use super::{DiversifiableFullViewingKey, FullViewingKey};
345 use crate::{constants::SPENDING_KEY_GENERATOR, zip32};
346
347 #[test]
348 fn ak_must_be_prime_order() {
349 let mut buf = [0; 96];
350 let identity = ironfish_jubjub::SubgroupPoint::identity();
351
352 buf[0..32].copy_from_slice(&identity.to_bytes());
354 buf[32..64].copy_from_slice(&identity.to_bytes());
355
356 assert_eq!(
358 FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
359 "ak not of prime order"
360 );
361
362 let basepoint = &*SPENDING_KEY_GENERATOR;
364 buf[0..32].copy_from_slice(&basepoint.to_bytes());
365
366 assert!(FullViewingKey::read(&buf[..]).is_ok());
368 }
369
370 #[test]
371 fn dfvk_round_trip() {
372 let dfvk = {
373 let extsk = zip32::ExtendedSpendingKey::master(&[]);
374 let extfvk = zip32::ExtendedFullViewingKey::from(&extsk);
375 DiversifiableFullViewingKey::from(extfvk)
376 };
377
378 let dfvk_bytes = dfvk.to_bytes();
380 let dfvk_parsed = DiversifiableFullViewingKey::from_bytes(&dfvk_bytes).unwrap();
381 assert_eq!(dfvk_parsed.fvk.vk.ak, dfvk.fvk.vk.ak);
382 assert_eq!(dfvk_parsed.fvk.vk.nk, dfvk.fvk.vk.nk);
383 assert_eq!(dfvk_parsed.fvk.ovk, dfvk.fvk.ovk);
384 assert_eq!(dfvk_parsed.dk, dfvk.dk);
385
386 assert_eq!(dfvk_parsed.to_bytes(), dfvk_bytes);
388 }
389}