|
| 1 | +//! Public-key views: the domain public key, per-account derived keys, and the |
| 2 | +//! latest key version. |
| 3 | +
|
| 4 | +use crate::crypto_shared::{ |
| 5 | + derive_key_secp256k1, kdf::derive_public_key_edwards_point_ed25519, types::PublicKeyExtended, |
| 6 | +}; |
| 7 | +use crate::errors::Error; |
| 8 | +use crate::errors::PublicKeyError; |
| 9 | +use near_mpc_contract_interface::types::kdf::derive_tweak; |
| 10 | +use near_mpc_contract_interface::types::{self as dtos}; |
| 11 | + |
| 12 | +use dtos::{Curve, DomainId}; |
| 13 | +use near_sdk::{AccountId, env, near}; |
| 14 | + |
| 15 | +use crate::{MpcContract, MpcContractExt}; |
| 16 | + |
| 17 | +#[near] |
| 18 | +impl MpcContract { |
| 19 | + /// This is the root public key combined from all the public keys of the participants. |
| 20 | + /// The domain parameter specifies which domain we're querying the public key for; |
| 21 | + /// the default is the first domain. |
| 22 | + #[handle_result] |
| 23 | + pub fn public_key(&self, domain_id: Option<DomainId>) -> Result<dtos::PublicKey, Error> { |
| 24 | + let domain_id = domain_id.unwrap_or_else(DomainId::legacy_ecdsa_id); |
| 25 | + self.public_key_extended(domain_id).map(Into::into) |
| 26 | + } |
| 27 | + |
| 28 | + /// This is the derived public key of the caller given path and predecessor |
| 29 | + /// if predecessor is not provided, it will be the caller of the contract. |
| 30 | + /// |
| 31 | + /// The domain parameter specifies which domain we're deriving the public key for; |
| 32 | + /// the default is the first domain. |
| 33 | + #[handle_result] |
| 34 | + pub fn derived_public_key( |
| 35 | + &self, |
| 36 | + path: String, |
| 37 | + predecessor: Option<AccountId>, |
| 38 | + domain_id: Option<DomainId>, |
| 39 | + ) -> Result<dtos::PublicKey, Error> { |
| 40 | + let predecessor: AccountId = predecessor.unwrap_or_else(env::predecessor_account_id); |
| 41 | + let tweak = derive_tweak(&predecessor, &path); |
| 42 | + |
| 43 | + let domain = domain_id.unwrap_or_else(DomainId::legacy_ecdsa_id); |
| 44 | + let public_key = self.public_key_extended(domain)?; |
| 45 | + |
| 46 | + let derived_public_key: dtos::PublicKey = match public_key { |
| 47 | + PublicKeyExtended::Secp256k1 { near_public_key } => { |
| 48 | + let secp_pk = dtos::Secp256k1PublicKey::try_from(&near_public_key) |
| 49 | + .expect("Secp256k1 variant always has a secp256k1 key"); |
| 50 | + let affine = *k256::PublicKey::try_from(&secp_pk) |
| 51 | + .expect("stored key is always valid") |
| 52 | + .as_affine(); |
| 53 | + let derived_public_key = |
| 54 | + derive_key_secp256k1(&affine, &tweak).map_err(PublicKeyError::from)?; |
| 55 | + derived_public_key.into() |
| 56 | + } |
| 57 | + PublicKeyExtended::Ed25519 { edwards_point, .. } => { |
| 58 | + let derived_public_key_edwards_point = |
| 59 | + derive_public_key_edwards_point_ed25519(&edwards_point, &tweak); |
| 60 | + dtos::Ed25519PublicKey::from(derived_public_key_edwards_point.compress()).into() |
| 61 | + } |
| 62 | + PublicKeyExtended::Bls12381 { public_key } => public_key, |
| 63 | + }; |
| 64 | + |
| 65 | + Ok(derived_public_key) |
| 66 | + } |
| 67 | + |
| 68 | + /// Key versions refer new versions of the root key that we may choose to generate on cohort |
| 69 | + /// changes. Older key versions will always work but newer key versions were never held by |
| 70 | + /// older signers. Newer key versions may also add new security features, like only existing |
| 71 | + /// within a secure enclave. The signature_scheme parameter specifies which protocol |
| 72 | + /// we're querying the latest version for. The default is Secp256k1. The default is **NOT** |
| 73 | + /// to query across all protocols. |
| 74 | + pub fn latest_key_version(&self, signature_scheme: Option<Curve>) -> u32 { |
| 75 | + self.protocol_state |
| 76 | + .most_recent_domain_for_curve(signature_scheme.unwrap_or_default()) |
| 77 | + .unwrap() |
| 78 | + .0 as u32 |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +impl MpcContract { |
| 83 | + pub(crate) fn public_key_extended( |
| 84 | + &self, |
| 85 | + domain_id: DomainId, |
| 86 | + ) -> Result<PublicKeyExtended, Error> { |
| 87 | + self.protocol_state.public_key(domain_id) |
| 88 | + } |
| 89 | +} |
0 commit comments