Skip to main content

ic_canister_kit/
ecdsa.rs

1use candid::CandidType;
2pub use ic_cdk_management_canister::{
3    EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, EcdsaPublicKeyResult, SignWithEcdsaArgs, SignWithEcdsaResult,
4};
5use serde::{Deserialize, Serialize};
6
7use crate::{identity::CanisterId, types::CanisterCallError};
8
9/// 私钥派生路径
10/// 不知道有没有长度要求的
11pub type EcdsaDerivationPath = Vec<Vec<u8>>;
12
13// #[derive(Debug)]
14// pub struct EcdsaDerivationPath(Vec<Vec<u8>>);
15
16// #[derive(Debug)]
17// pub enum EcdsaDerivationPathError {
18//     WrongLength,     // len <= 256
19//     WrongPathLength, // len <= 256
20// }
21// impl std::fmt::Display for EcdsaDerivationPathError {
22//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23//         match self {
24//             EcdsaDerivationPathError::WrongLength => write!(f, "wrong length"),
25//             EcdsaDerivationPathError::WrongPathLength => write!(f, "wrong path length"),
26//         }
27//     }
28// }
29// impl std::error::Error for EcdsaDerivationPathError {}
30
31// impl TryFrom<Vec<Vec<u8>>> for EcdsaDerivationPath {
32//     type Error = EcdsaDerivationPathError;
33
34//     fn try_from(value: Vec<Vec<u8>>) -> Result<Self, Self::Error> {
35//         if 256 < value.len() {
36//             return Err(EcdsaDerivationPathError::WrongLength);
37//         }
38//         for path in &value {
39//             if 256 < path.len() {
40//                 return Err(EcdsaDerivationPathError::WrongPathLength);
41//             }
42//         }
43//         Ok(EcdsaDerivationPath(value))
44//     }
45// }
46
47/// 罐子管理的私钥路径,确定使用哪一个私钥
48#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
49pub struct EcdsaIdentity {
50    /// 加密曲线
51    pub key_id: EcdsaKeyId,
52
53    /// 派生路径
54    pub derivation_path: EcdsaDerivationPath,
55}
56
57/// 消息 hash 对象,必须 32 长度
58#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
59pub struct MessageHash(Vec<u8>);
60
61/// 消息 hash 错误
62#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
63pub struct MessageHashError;
64impl std::fmt::Display for MessageHashError {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(f, "Hash of the message with length of 32 bytes.")
67    }
68}
69impl std::error::Error for MessageHashError {}
70impl TryFrom<Vec<u8>> for MessageHash {
71    type Error = MessageHashError;
72
73    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
74        if value.len() != 32 {
75            return Err(MessageHashError);
76        }
77        Ok(MessageHash(value))
78    }
79}
80
81/// 查询公钥
82/// https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key
83pub async fn ecdsa_public_key(
84    canister_id: Option<CanisterId>, // 不写则是自身 id
85    identity: EcdsaIdentity,
86) -> super::types::CanisterCallResult<EcdsaPublicKeyResult> {
87    ic_cdk_management_canister::ecdsa_public_key(&EcdsaPublicKeyArgs {
88        canister_id,
89        // derivation_path: identity.derivation_path.0,
90        derivation_path: identity.derivation_path,
91        key_id: identity.key_id,
92    })
93    .await
94    .map_err(|err| CanisterCallError {
95        canister_id: crate::identity::CanisterId::management_canister(),
96        method: "ic#ecdsa_public_key".to_string(),
97        message: err.to_string(),
98    })
99}
100
101/// 签名
102/// https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_ecdsa
103pub async fn sign_with_ecdsa(
104    identity: EcdsaIdentity,
105    message_hash: MessageHash,
106) -> super::types::CanisterCallResult<SignWithEcdsaResult> {
107    ic_cdk_management_canister::sign_with_ecdsa(&SignWithEcdsaArgs {
108        message_hash: message_hash.0,
109        derivation_path: identity.derivation_path,
110        key_id: identity.key_id,
111    })
112    .await
113    .map_err(|err| CanisterCallError {
114        canister_id: crate::identity::CanisterId::management_canister(),
115        method: "ic#sign_with_ecdsa".to_string(),
116        message: err.to_string(),
117    })
118}