1#![forbid(unsafe_code)]
2#![forbid(missing_docs)]
3#![warn(clippy::all)]
4#![warn(clippy::nursery)]
5#![warn(future_incompatible)]
6#![warn(rust_2018_idioms)]
7#![warn(rustdoc::broken_intra_doc_links)]
8#![warn(rustdoc::missing_crate_level_docs)]
9#![deny(unused_must_use)]
10#![deny(unused_results)]
11
12#[cfg(not(any(feature = "secp256k1", feature = "ed25519", feature = "vetkeys")))]
31compile_error!("At least one of the features (secp256k1, ed25519, vetkeys) must be enabled");
32
33pub use ic_management_canister_types::{
34 CanisterId, EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, EcdsaPublicKeyResult, SchnorrAlgorithm,
35 SchnorrKeyId, SchnorrPublicKeyArgs, SchnorrPublicKeyResult, VetKDCurve, VetKDKeyId,
36 VetKDPublicKeyArgs, VetKDPublicKeyResult,
37};
38
39#[derive(Copy, Clone, Debug, Eq, PartialEq)]
41pub enum Error {
42 UnknownKeyIdentifier,
44 AlgorithmNotSupported,
46 CanisterIdMissing,
50 InvalidPath,
55}
56
57enum MasterPublicKeyInner {
58 #[cfg(feature = "secp256k1")]
59 EcdsaSecp256k1(ic_secp256k1::PublicKey),
60 #[cfg(feature = "secp256k1")]
61 Bip340Secp256k1(ic_secp256k1::PublicKey),
62 #[cfg(feature = "ed25519")]
63 Ed25519(ic_ed25519::PublicKey),
64 #[cfg(feature = "vetkeys")]
65 VetKD(ic_vetkeys::MasterPublicKey),
66}
67
68pub struct MasterPublicKey {
70 inner: MasterPublicKeyInner,
71}
72
73impl MasterPublicKey {
74 pub fn derive_canister_key(&self, canister_id: &CanisterId) -> CanisterMasterKey {
76 let inner = match &self.inner {
77 #[cfg(feature = "secp256k1")]
78 MasterPublicKeyInner::EcdsaSecp256k1(mk) => {
79 let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
80 canister_id.as_slice().to_vec(),
81 )]);
82 DerivedPublicKeyInner::EcdsaSecp256k1(mk.derive_subkey(&path))
83 }
84 #[cfg(feature = "secp256k1")]
85 MasterPublicKeyInner::Bip340Secp256k1(mk) => {
86 let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
87 canister_id.as_slice().to_vec(),
88 )]);
89 DerivedPublicKeyInner::Bip340Secp256k1(mk.derive_subkey(&path))
90 }
91 #[cfg(feature = "ed25519")]
92 MasterPublicKeyInner::Ed25519(mk) => {
93 let path = ic_ed25519::DerivationPath::new(vec![ic_ed25519::DerivationIndex(
94 canister_id.as_slice().to_vec(),
95 )]);
96 DerivedPublicKeyInner::Ed25519(mk.derive_subkey(&path))
97 }
98 #[cfg(feature = "vetkeys")]
99 MasterPublicKeyInner::VetKD(mk) => {
100 DerivedPublicKeyInner::VetKD(mk.derive_canister_key(canister_id.as_slice()))
101 }
102 };
103 CanisterMasterKey { inner }
104 }
105}
106
107impl TryFrom<&EcdsaKeyId> for MasterPublicKey {
108 type Error = Error;
109
110 fn try_from(key_id: &EcdsaKeyId) -> Result<Self, Self::Error> {
111 if key_id.curve != EcdsaCurve::Secp256k1 {
112 return Err(Error::AlgorithmNotSupported);
113 }
114
115 #[cfg(feature = "secp256k1")]
116 {
117 let mk = match (key_id.curve, key_id.name.as_ref()) {
118 (EcdsaCurve::Secp256k1, "key_1") => {
119 ic_secp256k1::PublicKey::mainnet_key(ic_secp256k1::MasterPublicKeyId::EcdsaKey1)
120 }
121 (EcdsaCurve::Secp256k1, "test_key_1") => ic_secp256k1::PublicKey::mainnet_key(
122 ic_secp256k1::MasterPublicKeyId::EcdsaTestKey1,
123 ),
124 (EcdsaCurve::Secp256k1, "pocketic_key_1") => ic_secp256k1::PublicKey::pocketic_key(
125 ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaKey1,
126 ),
127 (EcdsaCurve::Secp256k1, "pocketic_test_key_1") => {
128 ic_secp256k1::PublicKey::pocketic_key(
129 ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaTestKey1,
130 )
131 }
132 (EcdsaCurve::Secp256k1, "dfx_test_key") => ic_secp256k1::PublicKey::pocketic_key(
133 ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaDfxTestKey,
134 ),
135 (_, _) => return Err(Error::UnknownKeyIdentifier),
136 };
137
138 let inner = MasterPublicKeyInner::EcdsaSecp256k1(mk);
139 Ok(Self { inner })
140 }
141
142 #[cfg(not(feature = "secp256k1"))]
143 {
144 Err(Error::AlgorithmNotSupported)
145 }
146 }
147}
148
149impl TryFrom<&SchnorrKeyId> for MasterPublicKey {
150 type Error = Error;
151
152 fn try_from(key_id: &SchnorrKeyId) -> Result<Self, Self::Error> {
153 #[cfg(feature = "secp256k1")]
154 {
155 if key_id.algorithm == SchnorrAlgorithm::Bip340secp256k1 {
156 let mk = match key_id.name.as_ref() {
157 "key_1" => ic_secp256k1::PublicKey::mainnet_key(
158 ic_secp256k1::MasterPublicKeyId::SchnorrKey1,
159 ),
160 "test_key_1" => ic_secp256k1::PublicKey::mainnet_key(
161 ic_secp256k1::MasterPublicKeyId::SchnorrTestKey1,
162 ),
163 "pocketic_key_1" => ic_secp256k1::PublicKey::pocketic_key(
164 ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrKey1,
165 ),
166 "pocketic_test_key_1" => ic_secp256k1::PublicKey::pocketic_key(
167 ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrTestKey1,
168 ),
169 "dfx_test_key" => ic_secp256k1::PublicKey::pocketic_key(
170 ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrDfxTestKey,
171 ),
172 _ => return Err(Error::UnknownKeyIdentifier),
173 };
174
175 let inner = MasterPublicKeyInner::Bip340Secp256k1(mk);
176 return Ok(Self { inner });
177 }
178 }
179
180 #[cfg(feature = "ed25519")]
181 {
182 if key_id.algorithm == SchnorrAlgorithm::Ed25519 {
183 let mk = match key_id.name.as_ref() {
184 "key_1" => {
185 ic_ed25519::PublicKey::mainnet_key(ic_ed25519::MasterPublicKeyId::Key1)
186 }
187 "test_key_1" => {
188 ic_ed25519::PublicKey::mainnet_key(ic_ed25519::MasterPublicKeyId::TestKey1)
189 }
190 "pocketic_key_1" => ic_ed25519::PublicKey::pocketic_key(
191 ic_ed25519::PocketIcMasterPublicKeyId::Key1,
192 ),
193 "pocketic_test_key_1" => ic_ed25519::PublicKey::pocketic_key(
194 ic_ed25519::PocketIcMasterPublicKeyId::TestKey1,
195 ),
196 "dfx_test_key" => ic_ed25519::PublicKey::pocketic_key(
197 ic_ed25519::PocketIcMasterPublicKeyId::DfxTestKey,
198 ),
199 _ => return Err(Error::UnknownKeyIdentifier),
200 };
201
202 let inner = MasterPublicKeyInner::Ed25519(mk);
203 return Ok(Self { inner });
204 }
205 }
206
207 Err(Error::AlgorithmNotSupported)
208 }
209}
210
211impl TryFrom<&VetKDKeyId> for MasterPublicKey {
212 type Error = Error;
213
214 fn try_from(key_id: &VetKDKeyId) -> Result<Self, Self::Error> {
215 #[cfg(feature = "vetkeys")]
216 {
217 if let Some(mk) = ic_vetkeys::MasterPublicKey::for_mainnet_key(key_id) {
218 let inner = MasterPublicKeyInner::VetKD(mk);
219 return Ok(Self { inner });
220 }
221 }
222
223 Err(Error::AlgorithmNotSupported)
224 }
225}
226
227enum DerivedPublicKeyInner {
228 #[cfg(feature = "secp256k1")]
229 EcdsaSecp256k1((ic_secp256k1::PublicKey, [u8; 32])),
230 #[cfg(feature = "secp256k1")]
231 Bip340Secp256k1((ic_secp256k1::PublicKey, [u8; 32])),
232 #[cfg(feature = "ed25519")]
233 Ed25519((ic_ed25519::PublicKey, [u8; 32])),
234 #[cfg(feature = "vetkeys")]
235 VetKD(ic_vetkeys::DerivedPublicKey),
236}
237
238pub struct CanisterMasterKey {
243 inner: DerivedPublicKeyInner,
244}
245
246impl CanisterMasterKey {
247 pub fn derive_key_with_context(&self, context: &[u8]) -> DerivedPublicKey {
257 let inner = match &self.inner {
258 #[cfg(feature = "secp256k1")]
259 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => {
260 let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
261 context.to_vec(),
262 )]);
263 DerivedPublicKeyInner::EcdsaSecp256k1(
264 ck.0.derive_subkey_with_chain_code(&path, &ck.1),
265 )
266 }
267 #[cfg(feature = "secp256k1")]
268 DerivedPublicKeyInner::Bip340Secp256k1(ck) => {
269 let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
270 context.to_vec(),
271 )]);
272 DerivedPublicKeyInner::Bip340Secp256k1(
273 ck.0.derive_subkey_with_chain_code(&path, &ck.1),
274 )
275 }
276 #[cfg(feature = "ed25519")]
277 DerivedPublicKeyInner::Ed25519(ck) => {
278 let path = ic_ed25519::DerivationPath::new(vec![ic_ed25519::DerivationIndex(
279 context.to_vec(),
280 )]);
281 DerivedPublicKeyInner::Ed25519(ck.0.derive_subkey_with_chain_code(&path, &ck.1))
282 }
283 #[cfg(feature = "vetkeys")]
284 DerivedPublicKeyInner::VetKD(ck) => {
285 DerivedPublicKeyInner::VetKD(ck.derive_sub_key(context))
286 }
287 };
288 DerivedPublicKey { inner }
289 }
290
291 pub fn derive_key(&self, path: &[Vec<u8>]) -> Result<DerivedPublicKey, Error> {
296 let inner = match &self.inner {
297 #[cfg(feature = "secp256k1")]
298 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => {
299 let path = ic_secp256k1::DerivationPath::new(
300 path.iter()
301 .cloned()
302 .map(ic_secp256k1::DerivationIndex)
303 .collect(),
304 );
305 DerivedPublicKeyInner::EcdsaSecp256k1(
306 ck.0.derive_subkey_with_chain_code(&path, &ck.1),
307 )
308 }
309 #[cfg(feature = "secp256k1")]
310 DerivedPublicKeyInner::Bip340Secp256k1(ck) => {
311 let path = ic_secp256k1::DerivationPath::new(
312 path.iter()
313 .cloned()
314 .map(ic_secp256k1::DerivationIndex)
315 .collect(),
316 );
317 DerivedPublicKeyInner::Bip340Secp256k1(
318 ck.0.derive_subkey_with_chain_code(&path, &ck.1),
319 )
320 }
321 #[cfg(feature = "ed25519")]
322 DerivedPublicKeyInner::Ed25519(ck) => {
323 let path = ic_ed25519::DerivationPath::new(
324 path.iter()
325 .cloned()
326 .map(ic_ed25519::DerivationIndex)
327 .collect(),
328 );
329 DerivedPublicKeyInner::Ed25519(ck.0.derive_subkey_with_chain_code(&path, &ck.1))
330 }
331 #[cfg(feature = "vetkeys")]
332 DerivedPublicKeyInner::VetKD(_ck) => {
333 return Err(Error::AlgorithmNotSupported);
337 }
338 };
339 Ok(DerivedPublicKey { inner })
340 }
341
342 pub fn serialize(&self) -> Vec<u8> {
344 match &self.inner {
345 #[cfg(feature = "secp256k1")]
346 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => ck.0.serialize_sec1(true),
347 #[cfg(feature = "secp256k1")]
348 DerivedPublicKeyInner::Bip340Secp256k1(ck) => ck.0.serialize_sec1(true),
349 #[cfg(feature = "ed25519")]
350 DerivedPublicKeyInner::Ed25519(ck) => ck.0.serialize_raw().to_vec(),
351 #[cfg(feature = "vetkeys")]
352 DerivedPublicKeyInner::VetKD(ck) => ck.serialize(),
353 }
354 }
355
356 pub fn chain_code(&self) -> Option<Vec<u8>> {
360 match &self.inner {
361 #[cfg(feature = "secp256k1")]
362 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => Some(ck.1.to_vec()),
363 #[cfg(feature = "secp256k1")]
364 DerivedPublicKeyInner::Bip340Secp256k1(ck) => Some(ck.1.to_vec()),
365 #[cfg(feature = "ed25519")]
366 DerivedPublicKeyInner::Ed25519(ck) => Some(ck.1.to_vec()),
367 #[cfg(feature = "vetkeys")]
368 DerivedPublicKeyInner::VetKD(_ck) => None,
369 }
370 }
371}
372
373pub struct DerivedPublicKey {
375 inner: DerivedPublicKeyInner,
376}
377
378impl DerivedPublicKey {
379 pub fn serialize(&self) -> Vec<u8> {
381 match &self.inner {
382 #[cfg(feature = "secp256k1")]
383 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => ck.0.serialize_sec1(true),
384 #[cfg(feature = "secp256k1")]
385 DerivedPublicKeyInner::Bip340Secp256k1(ck) => ck.0.serialize_sec1(true),
386 #[cfg(feature = "ed25519")]
387 DerivedPublicKeyInner::Ed25519(ck) => ck.0.serialize_raw().to_vec(),
388 #[cfg(feature = "vetkeys")]
389 DerivedPublicKeyInner::VetKD(ck) => ck.serialize(),
390 }
391 }
392
393 pub fn chain_code(&self) -> Option<Vec<u8>> {
397 match &self.inner {
398 #[cfg(feature = "secp256k1")]
399 DerivedPublicKeyInner::EcdsaSecp256k1(ck) => Some(ck.1.to_vec()),
400 #[cfg(feature = "secp256k1")]
401 DerivedPublicKeyInner::Bip340Secp256k1(ck) => Some(ck.1.to_vec()),
402 #[cfg(feature = "ed25519")]
403 DerivedPublicKeyInner::Ed25519(ck) => Some(ck.1.to_vec()),
404 #[cfg(feature = "vetkeys")]
405 DerivedPublicKeyInner::VetKD(_ck) => None,
406 }
407 }
408}
409
410pub fn derive_ecdsa_key(args: &EcdsaPublicKeyArgs) -> Result<EcdsaPublicKeyResult, Error> {
431 let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
432
433 let dk = MasterPublicKey::try_from(&args.key_id)?
434 .derive_canister_key(&canister_id)
435 .derive_key(&args.derivation_path)?;
436
437 Ok(EcdsaPublicKeyResult {
438 public_key: dk.serialize(),
439 chain_code: dk.chain_code().expect("Missing chain code"),
440 })
441}
442
443pub fn derive_schnorr_key(args: &SchnorrPublicKeyArgs) -> Result<SchnorrPublicKeyResult, Error> {
462 let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
463
464 let dk = MasterPublicKey::try_from(&args.key_id)?
465 .derive_canister_key(&canister_id)
466 .derive_key(&args.derivation_path)?;
467
468 Ok(SchnorrPublicKeyResult {
469 public_key: dk.serialize(),
470 chain_code: dk.chain_code().expect("Missing chain code"),
471 })
472}
473
474pub fn derive_vetkd_key(args: &VetKDPublicKeyArgs) -> Result<VetKDPublicKeyResult, Error> {
480 let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
481
482 let ck = MasterPublicKey::try_from(&args.key_id)?.derive_canister_key(&canister_id);
483
484 let dk = ck.derive_key_with_context(&args.context);
485 Ok(VetKDPublicKeyResult {
486 public_key: dk.serialize(),
487 })
488}