#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![warn(clippy::all)]
#![warn(clippy::nursery)]
#![warn(future_incompatible)]
#![warn(rust_2018_idioms)]
#![warn(rustdoc::broken_intra_doc_links)]
#![warn(rustdoc::missing_crate_level_docs)]
#![deny(unused_must_use)]
#![deny(unused_results)]
#[cfg(not(any(feature = "secp256k1", feature = "ed25519", feature = "vetkeys")))]
compile_error!("At least one of the features (secp256k1, ed25519, vetkeys) must be enabled");
pub use ic_management_canister_types::{
CanisterId, EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, EcdsaPublicKeyResult, SchnorrAlgorithm,
SchnorrKeyId, SchnorrPublicKeyArgs, SchnorrPublicKeyResult, VetKDCurve, VetKDKeyId,
VetKDPublicKeyArgs, VetKDPublicKeyResult,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
UnknownKeyIdentifier,
AlgorithmNotSupported,
CanisterIdMissing,
InvalidPath,
}
enum MasterPublicKeyInner {
#[cfg(feature = "secp256k1")]
EcdsaSecp256k1(ic_secp256k1::PublicKey),
#[cfg(feature = "secp256k1")]
Bip340Secp256k1(ic_secp256k1::PublicKey),
#[cfg(feature = "ed25519")]
Ed25519(ic_ed25519::PublicKey),
#[cfg(feature = "vetkeys")]
VetKD(ic_vetkeys::MasterPublicKey),
}
pub struct MasterPublicKey {
inner: MasterPublicKeyInner,
}
impl MasterPublicKey {
pub fn derive_canister_key(&self, canister_id: &CanisterId) -> CanisterMasterKey {
let inner = match &self.inner {
#[cfg(feature = "secp256k1")]
MasterPublicKeyInner::EcdsaSecp256k1(mk) => {
let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
canister_id.as_slice().to_vec(),
)]);
DerivedPublicKeyInner::EcdsaSecp256k1(mk.derive_subkey(&path))
}
#[cfg(feature = "secp256k1")]
MasterPublicKeyInner::Bip340Secp256k1(mk) => {
let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
canister_id.as_slice().to_vec(),
)]);
DerivedPublicKeyInner::Bip340Secp256k1(mk.derive_subkey(&path))
}
#[cfg(feature = "ed25519")]
MasterPublicKeyInner::Ed25519(mk) => {
let path = ic_ed25519::DerivationPath::new(vec![ic_ed25519::DerivationIndex(
canister_id.as_slice().to_vec(),
)]);
DerivedPublicKeyInner::Ed25519(mk.derive_subkey(&path))
}
#[cfg(feature = "vetkeys")]
MasterPublicKeyInner::VetKD(mk) => {
DerivedPublicKeyInner::VetKD(mk.derive_canister_key(canister_id.as_slice()))
}
};
CanisterMasterKey { inner }
}
}
impl TryFrom<&EcdsaKeyId> for MasterPublicKey {
type Error = Error;
fn try_from(key_id: &EcdsaKeyId) -> Result<Self, Self::Error> {
if key_id.curve != EcdsaCurve::Secp256k1 {
return Err(Error::AlgorithmNotSupported);
}
#[cfg(feature = "secp256k1")]
{
let mk = match (key_id.curve, key_id.name.as_ref()) {
(EcdsaCurve::Secp256k1, "key_1") => {
ic_secp256k1::PublicKey::mainnet_key(ic_secp256k1::MasterPublicKeyId::EcdsaKey1)
}
(EcdsaCurve::Secp256k1, "test_key_1") => ic_secp256k1::PublicKey::mainnet_key(
ic_secp256k1::MasterPublicKeyId::EcdsaTestKey1,
),
(EcdsaCurve::Secp256k1, "pocketic_key_1") => ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaKey1,
),
(EcdsaCurve::Secp256k1, "pocketic_test_key_1") => {
ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaTestKey1,
)
}
(EcdsaCurve::Secp256k1, "dfx_test_key") => ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::EcdsaDfxTestKey,
),
(_, _) => return Err(Error::UnknownKeyIdentifier),
};
let inner = MasterPublicKeyInner::EcdsaSecp256k1(mk);
Ok(Self { inner })
}
#[cfg(not(feature = "secp256k1"))]
{
Err(Error::AlgorithmNotSupported)
}
}
}
impl TryFrom<&SchnorrKeyId> for MasterPublicKey {
type Error = Error;
fn try_from(key_id: &SchnorrKeyId) -> Result<Self, Self::Error> {
#[cfg(feature = "secp256k1")]
{
if key_id.algorithm == SchnorrAlgorithm::Bip340secp256k1 {
let mk = match key_id.name.as_ref() {
"key_1" => ic_secp256k1::PublicKey::mainnet_key(
ic_secp256k1::MasterPublicKeyId::SchnorrKey1,
),
"test_key_1" => ic_secp256k1::PublicKey::mainnet_key(
ic_secp256k1::MasterPublicKeyId::SchnorrTestKey1,
),
"pocketic_key_1" => ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrKey1,
),
"pocketic_test_key_1" => ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrTestKey1,
),
"dfx_test_key" => ic_secp256k1::PublicKey::pocketic_key(
ic_secp256k1::PocketIcMasterPublicKeyId::SchnorrDfxTestKey,
),
_ => return Err(Error::UnknownKeyIdentifier),
};
let inner = MasterPublicKeyInner::Bip340Secp256k1(mk);
return Ok(Self { inner });
}
}
#[cfg(feature = "ed25519")]
{
if key_id.algorithm == SchnorrAlgorithm::Ed25519 {
let mk = match key_id.name.as_ref() {
"key_1" => {
ic_ed25519::PublicKey::mainnet_key(ic_ed25519::MasterPublicKeyId::Key1)
}
"test_key_1" => {
ic_ed25519::PublicKey::mainnet_key(ic_ed25519::MasterPublicKeyId::TestKey1)
}
"pocketic_key_1" => ic_ed25519::PublicKey::pocketic_key(
ic_ed25519::PocketIcMasterPublicKeyId::Key1,
),
"pocketic_test_key_1" => ic_ed25519::PublicKey::pocketic_key(
ic_ed25519::PocketIcMasterPublicKeyId::TestKey1,
),
"dfx_test_key" => ic_ed25519::PublicKey::pocketic_key(
ic_ed25519::PocketIcMasterPublicKeyId::DfxTestKey,
),
_ => return Err(Error::UnknownKeyIdentifier),
};
let inner = MasterPublicKeyInner::Ed25519(mk);
return Ok(Self { inner });
}
}
Err(Error::AlgorithmNotSupported)
}
}
impl TryFrom<&VetKDKeyId> for MasterPublicKey {
type Error = Error;
fn try_from(key_id: &VetKDKeyId) -> Result<Self, Self::Error> {
#[cfg(feature = "vetkeys")]
{
if let Some(mk) = ic_vetkeys::MasterPublicKey::for_mainnet_key(key_id) {
let inner = MasterPublicKeyInner::VetKD(mk);
return Ok(Self { inner });
}
}
Err(Error::AlgorithmNotSupported)
}
}
enum DerivedPublicKeyInner {
#[cfg(feature = "secp256k1")]
EcdsaSecp256k1((ic_secp256k1::PublicKey, [u8; 32])),
#[cfg(feature = "secp256k1")]
Bip340Secp256k1((ic_secp256k1::PublicKey, [u8; 32])),
#[cfg(feature = "ed25519")]
Ed25519((ic_ed25519::PublicKey, [u8; 32])),
#[cfg(feature = "vetkeys")]
VetKD(ic_vetkeys::DerivedPublicKey),
}
pub struct CanisterMasterKey {
inner: DerivedPublicKeyInner,
}
impl CanisterMasterKey {
pub fn derive_key_with_context(&self, context: &[u8]) -> DerivedPublicKey {
let inner = match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => {
let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
context.to_vec(),
)]);
DerivedPublicKeyInner::EcdsaSecp256k1(
ck.0.derive_subkey_with_chain_code(&path, &ck.1),
)
}
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => {
let path = ic_secp256k1::DerivationPath::new(vec![ic_secp256k1::DerivationIndex(
context.to_vec(),
)]);
DerivedPublicKeyInner::Bip340Secp256k1(
ck.0.derive_subkey_with_chain_code(&path, &ck.1),
)
}
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => {
let path = ic_ed25519::DerivationPath::new(vec![ic_ed25519::DerivationIndex(
context.to_vec(),
)]);
DerivedPublicKeyInner::Ed25519(ck.0.derive_subkey_with_chain_code(&path, &ck.1))
}
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(ck) => {
DerivedPublicKeyInner::VetKD(ck.derive_sub_key(context))
}
};
DerivedPublicKey { inner }
}
pub fn derive_key(&self, path: &[Vec<u8>]) -> Result<DerivedPublicKey, Error> {
let inner = match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => {
let path = ic_secp256k1::DerivationPath::new(
path.iter()
.cloned()
.map(ic_secp256k1::DerivationIndex)
.collect(),
);
DerivedPublicKeyInner::EcdsaSecp256k1(
ck.0.derive_subkey_with_chain_code(&path, &ck.1),
)
}
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => {
let path = ic_secp256k1::DerivationPath::new(
path.iter()
.cloned()
.map(ic_secp256k1::DerivationIndex)
.collect(),
);
DerivedPublicKeyInner::Bip340Secp256k1(
ck.0.derive_subkey_with_chain_code(&path, &ck.1),
)
}
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => {
let path = ic_ed25519::DerivationPath::new(
path.iter()
.cloned()
.map(ic_ed25519::DerivationIndex)
.collect(),
);
DerivedPublicKeyInner::Ed25519(ck.0.derive_subkey_with_chain_code(&path, &ck.1))
}
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(_ck) => {
return Err(Error::AlgorithmNotSupported);
}
};
Ok(DerivedPublicKey { inner })
}
pub fn serialize(&self) -> Vec<u8> {
match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => ck.0.serialize_sec1(true),
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => ck.0.serialize_sec1(true),
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => ck.0.serialize_raw().to_vec(),
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(ck) => ck.serialize(),
}
}
pub fn chain_code(&self) -> Option<Vec<u8>> {
match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(_ck) => None,
}
}
}
pub struct DerivedPublicKey {
inner: DerivedPublicKeyInner,
}
impl DerivedPublicKey {
pub fn serialize(&self) -> Vec<u8> {
match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => ck.0.serialize_sec1(true),
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => ck.0.serialize_sec1(true),
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => ck.0.serialize_raw().to_vec(),
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(ck) => ck.serialize(),
}
}
pub fn chain_code(&self) -> Option<Vec<u8>> {
match &self.inner {
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::EcdsaSecp256k1(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "secp256k1")]
DerivedPublicKeyInner::Bip340Secp256k1(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "ed25519")]
DerivedPublicKeyInner::Ed25519(ck) => Some(ck.1.to_vec()),
#[cfg(feature = "vetkeys")]
DerivedPublicKeyInner::VetKD(_ck) => None,
}
}
}
pub fn derive_ecdsa_key(args: &EcdsaPublicKeyArgs) -> Result<EcdsaPublicKeyResult, Error> {
let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
let dk = MasterPublicKey::try_from(&args.key_id)?
.derive_canister_key(&canister_id)
.derive_key(&args.derivation_path)?;
Ok(EcdsaPublicKeyResult {
public_key: dk.serialize(),
chain_code: dk.chain_code().expect("Missing chain code"),
})
}
pub fn derive_schnorr_key(args: &SchnorrPublicKeyArgs) -> Result<SchnorrPublicKeyResult, Error> {
let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
let dk = MasterPublicKey::try_from(&args.key_id)?
.derive_canister_key(&canister_id)
.derive_key(&args.derivation_path)?;
Ok(SchnorrPublicKeyResult {
public_key: dk.serialize(),
chain_code: dk.chain_code().expect("Missing chain code"),
})
}
pub fn derive_vetkd_key(args: &VetKDPublicKeyArgs) -> Result<VetKDPublicKeyResult, Error> {
let canister_id = args.canister_id.ok_or(Error::CanisterIdMissing)?;
let ck = MasterPublicKey::try_from(&args.key_id)?.derive_canister_key(&canister_id);
let dk = ck.derive_key_with_context(&args.context);
Ok(VetKDPublicKeyResult {
public_key: dk.serialize(),
})
}