penumbra_sdk_custody/threshold/
config.rs1use anyhow::Result;
2use decaf377::Fq;
3use decaf377_frost as frost;
4use ed25519_consensus::{SigningKey, VerificationKey};
5use penumbra_sdk_keys::{keys::NullifierKey, FullViewingKey};
6use rand_core::CryptoRngCore;
7use serde::{Deserialize, Serialize};
8use serde_with::{formats::Uppercase, hex::Hex, DisplayFromStr, TryFromInto};
9use std::collections::{HashMap, HashSet};
10
11#[serde_as]
13#[derive(Serialize, Deserialize)]
14struct SigningShareWrapper(#[serde_as(as = "Hex<Uppercase>")] Vec<u8>);
15
16impl From<frost::keys::SigningShare> for SigningShareWrapper {
17 fn from(value: frost::keys::SigningShare) -> Self {
18 Self(value.serialize())
19 }
20}
21
22impl TryFrom<SigningShareWrapper> for frost::keys::SigningShare {
23 type Error = anyhow::Error;
24
25 fn try_from(value: SigningShareWrapper) -> std::result::Result<Self, Self::Error> {
26 Ok(Self::deserialize(value.0)?)
27 }
28}
29
30#[serde_as]
32#[derive(Serialize, Deserialize)]
33struct VerifyingShareWrapper(#[serde_as(as = "Hex<Uppercase>")] Vec<u8>);
34
35impl From<frost::keys::VerifyingShare> for VerifyingShareWrapper {
36 fn from(value: frost::keys::VerifyingShare) -> Self {
37 Self(value.serialize())
38 }
39}
40
41impl TryFrom<VerifyingShareWrapper> for frost::keys::VerifyingShare {
42 type Error = anyhow::Error;
43
44 fn try_from(value: VerifyingShareWrapper) -> std::result::Result<Self, Self::Error> {
45 Ok(Self::deserialize(value.0)?)
46 }
47}
48
49#[serde_as]
51#[derive(Serialize, Deserialize)]
52struct SigningKeyWrapper(#[serde_as(as = "Hex<Uppercase>")] Vec<u8>);
53
54impl From<SigningKey> for SigningKeyWrapper {
55 fn from(value: SigningKey) -> Self {
56 Self(value.to_bytes().to_vec())
57 }
58}
59
60impl TryFrom<SigningKeyWrapper> for SigningKey {
61 type Error = anyhow::Error;
62
63 fn try_from(value: SigningKeyWrapper) -> std::result::Result<Self, Self::Error> {
64 Ok(Self::try_from(value.0.as_slice())?)
65 }
66}
67
68#[serde_as]
70#[derive(Serialize, Deserialize)]
71struct VerificationKeyWrapper(#[serde_as(as = "Hex<Uppercase>")] Vec<u8>);
72
73impl From<VerificationKey> for VerificationKeyWrapper {
74 fn from(value: VerificationKey) -> Self {
75 Self(value.to_bytes().to_vec())
76 }
77}
78
79impl TryFrom<VerificationKeyWrapper> for VerificationKey {
80 type Error = anyhow::Error;
81
82 fn try_from(value: VerificationKeyWrapper) -> std::result::Result<Self, Self::Error> {
83 Ok(Self::try_from(value.0.as_slice())?)
84 }
85}
86
87#[serde_as]
88#[derive(Clone, Debug, Serialize, Deserialize)]
89pub struct Config {
90 threshold: u16,
91 #[serde_as(as = "DisplayFromStr")]
92 fvk: FullViewingKey,
93 #[serde_as(as = "TryFromInto<SigningShareWrapper>")]
94 spend_key_share: frost::keys::SigningShare,
95 #[serde_as(as = "TryFromInto<SigningKeyWrapper>")]
96 signing_key: SigningKey,
97 #[serde_as(
98 as = "HashMap<TryFromInto<VerificationKeyWrapper>, TryFromInto<VerifyingShareWrapper>>"
99 )]
100 verifying_shares: HashMap<VerificationKey, frost::keys::VerifyingShare>,
101}
102
103impl PartialEq for Config {
104 fn eq(&self, other: &Self) -> bool {
105 self.threshold == other.threshold
106 && self.fvk == other.fvk
107 && self.spend_key_share == other.spend_key_share
108 && self.signing_key.as_bytes() == other.signing_key.as_bytes()
110 && self.verifying_shares == other.verifying_shares
111 }
112}
113
114impl Eq for Config {}
115
116impl Config {
117 pub(crate) fn from_parts(
119 key_package: frost::keys::KeyPackage,
120 public_key_package: frost::keys::PublicKeyPackage,
121 signing_key: SigningKey,
122 verification_keys: Vec<VerificationKey>,
123 nullifier_key: Fq,
124 ) -> Self {
125 let fvk = FullViewingKey::from_components(
126 public_key_package
127 .group_public()
128 .serialize()
129 .as_slice()
130 .try_into()
131 .expect("conversion of a group element to a VerifyingKey should not fail"),
132 NullifierKey(nullifier_key),
133 );
134 let spend_key_share = key_package.secret_share().to_owned();
135 let verifying_shares = verification_keys
136 .into_iter()
137 .map(|vk| {
138 let id = frost::Identifier::derive(vk.as_bytes().as_slice())
139 .expect("deriving identifiers should not fail");
140 (vk, public_key_package.signer_pubkeys()[&id])
141 })
142 .collect();
143 Self {
144 threshold: *key_package.min_signers(),
145 fvk,
146 spend_key_share,
147 signing_key,
148 verifying_shares,
149 }
150 }
151
152 pub fn deal(mut rng: &mut impl CryptoRngCore, t: u16, n: u16) -> Result<Vec<Self>> {
153 let signing_keys = (0..n)
154 .map(|_| {
155 let sk = SigningKey::new(&mut rng);
156 let pk = sk.verification_key();
157 (pk, sk)
158 })
159 .collect::<HashMap<_, _>>();
160 let identifiers = signing_keys
161 .keys()
162 .cloned()
163 .map(|pk| Ok((pk, frost::Identifier::derive(pk.as_bytes().as_slice())?)))
164 .collect::<Result<HashMap<_, _>, frost::Error>>()?;
165
166 let (share_map, public_key_package) = frost::keys::generate_with_dealer(
167 n,
168 t,
169 frost::keys::IdentifierList::Custom(
170 identifiers.values().cloned().collect::<Vec<_>>().as_slice(),
171 ),
172 &mut rng,
173 )?;
174 let verifying_shares = signing_keys
175 .keys()
176 .map(|pk| {
177 let identifier = frost::Identifier::derive(pk.to_bytes().as_slice())
178 .expect("should be able to derive identifier");
179 (pk.clone(), public_key_package.signer_pubkeys()[&identifier])
180 })
181 .collect::<HashMap<_, _>>();
182 let fvk = FullViewingKey::from_components(
185 public_key_package
186 .group_public()
187 .serialize()
188 .as_slice()
189 .try_into()
190 .expect("conversion of a group element to a VerifyingKey should not fail"),
191 NullifierKey(Fq::rand(rng)),
192 );
193
194 Ok(signing_keys
195 .into_iter()
196 .map(|(verification_key, signing_key)| {
197 let identifier = identifiers[&verification_key];
198 let signing_share = share_map[&identifier].value().clone();
199 Self {
200 threshold: t,
201 signing_key,
202 fvk: fvk.clone(),
203 spend_key_share: signing_share,
204 verifying_shares: verifying_shares.clone(),
205 }
206 })
207 .collect())
208 }
209
210 pub fn threshold(&self) -> u16 {
211 self.threshold
212 }
213
214 fn group_public(&self) -> frost::keys::VerifyingKey {
215 frost::keys::VerifyingKey::deserialize(
216 self.fvk.spend_verification_key().to_bytes().to_vec(),
217 )
218 .expect("should be able to parse out VerifyingKey from FullViewingKey")
219 }
220
221 pub fn key_package(&self) -> frost::keys::KeyPackage {
222 let identifier =
223 frost::Identifier::derive(&self.signing_key.verification_key().as_bytes().as_slice())
224 .expect("deriving our identifier should not fail");
225
226 frost::keys::KeyPackage::new(
227 identifier,
228 self.spend_key_share,
229 self.spend_key_share.into(),
230 self.group_public(),
231 self.threshold,
232 )
233 }
234
235 pub fn public_key_package(&self) -> frost::keys::PublicKeyPackage {
236 let signer_pubkeys = self
237 .verifying_shares
238 .iter()
239 .map(|(vk, share)| {
240 (
241 frost::Identifier::derive(vk.to_bytes().as_slice())
242 .expect("deriving an identifier should not fail"),
243 share.clone(),
244 )
245 })
246 .collect();
247 frost::keys::PublicKeyPackage::new(signer_pubkeys, self.group_public())
248 }
249
250 pub fn signing_key(&self) -> &SigningKey {
251 &self.signing_key
252 }
253
254 pub fn fvk(&self) -> &FullViewingKey {
255 &self.fvk
256 }
257
258 pub fn verification_keys(&self) -> HashSet<VerificationKey> {
259 self.verifying_shares.keys().cloned().collect()
260 }
261}
262
263#[cfg(test)]
264mod test {
265 use rand_core::OsRng;
266
267 use super::*;
268
269 #[test]
270 fn test_config_serialization_roundtrip() -> Result<()> {
271 let config = Config::deal(&mut OsRng, 2, 2)?.pop().unwrap();
273 let config_str = serde_json::to_string(&config)?;
274 let config2: Config = serde_json::from_str(&config_str)?;
275 assert_eq!(config.threshold, config2.threshold);
277 assert_eq!(config.fvk, config2.fvk);
278 assert_eq!(config.spend_key_share, config2.spend_key_share);
279 assert_eq!(config.verifying_shares, config2.verifying_shares);
280 Ok(())
281 }
282}