1+ use std:: vec;
2+
3+ use crate :: constants:: ECDSA_SIGN_COST ;
4+
5+ use bip32:: Seed ;
6+ use candid:: Principal ;
7+ use candid:: { CandidType , Deserialize } ;
8+ use ic_management_canister_types:: {
9+ SchnorrAlgorithm , SchnorrKeyId , SchnorrPublicKeyArgs , SchnorrPublicKeyResult ,
10+ SignWithSchnorrArgs , SignWithSchnorrResult ,
11+ } ;
12+ use serde:: Serialize ;
13+ use serde_bytes:: ByteBuf ;
14+ use sha2:: Digest ;
15+ #[ derive( CandidType , Deserialize , Serialize , Debug , Clone , PartialEq , Eq ) ]
16+ pub enum KeyType {
17+ ChainKey ,
18+ Native ( Vec < u8 > ) ,
19+ }
20+
21+ /// Fetches the ed25519 public key from the schnorr canister.
22+ pub async fn eddsa_public_key (
23+ key_type : KeyType ,
24+ key_name : String ,
25+ derivation_path : Vec < ByteBuf > ,
26+ ) -> Vec < u8 > {
27+ match key_type {
28+ KeyType :: ChainKey => {
29+ let res: Result < ( SchnorrPublicKeyResult , ) , _ > = ic_cdk:: call (
30+ Principal :: management_canister ( ) ,
31+ "schnorr_public_key" ,
32+ ( SchnorrPublicKeyArgs {
33+ canister_id : None ,
34+ derivation_path : derivation_path
35+ . iter ( )
36+ . map ( |p| p. clone ( ) . into_vec ( ) )
37+ . collect ( ) ,
38+ key_id : SchnorrKeyId {
39+ algorithm : SchnorrAlgorithm :: Ed25519 ,
40+ name : key_name,
41+ } ,
42+ } , ) ,
43+ )
44+ . await ;
45+
46+ res. unwrap ( ) . 0 . public_key
47+ }
48+ KeyType :: Native ( seed) => {
49+ let derivation_path = derivation_path_ed25519 ( & ic_cdk:: api:: id ( ) , & derivation_path) ;
50+ native_public_key_ed25519 ( Seed :: new ( seed. try_into ( ) . unwrap ( ) ) , derivation_path)
51+ }
52+ }
53+ }
54+
55+ fn native_public_key_ed25519 (
56+ seed : Seed ,
57+ derivation_path : ic_crypto_ed25519:: DerivationPath ,
58+ ) -> Vec < u8 > {
59+ let seed_32_bytes =
60+ <[ u8 ; 32 ] >:: try_from ( & seed. as_bytes ( ) [ 0 ..32 ] ) . expect ( "seed should be >= 32 bytes" ) ;
61+ let master_secret = ic_crypto_ed25519:: PrivateKey :: deserialize_raw_32 ( & seed_32_bytes) ;
62+ let ( derived_secret, _) = master_secret. derive_subkey ( & derivation_path) ;
63+ let public_key = derived_secret. public_key ( ) ;
64+ public_key. serialize_raw ( ) . to_vec ( )
65+ }
66+
67+ fn derivation_path_ed25519 (
68+ canister_id : & Principal ,
69+ derivation_path : & Vec < ByteBuf > ,
70+ ) -> ic_crypto_ed25519:: DerivationPath {
71+ let mut path = vec ! [ ] ;
72+ let derivation_index = ic_crypto_ed25519:: DerivationIndex ( canister_id. as_slice ( ) . to_vec ( ) ) ;
73+ path. push ( derivation_index) ;
74+
75+ for index in derivation_path {
76+ path. push ( ic_crypto_ed25519:: DerivationIndex ( index. to_vec ( ) ) ) ;
77+ }
78+ ic_crypto_ed25519:: DerivationPath :: new ( path)
79+ }
80+
81+ /// Signs a message with an ed25519 key.
82+ pub async fn sign_with_eddsa (
83+ key_type : & KeyType ,
84+ key_name : String ,
85+ derivation_path : Vec < ByteBuf > ,
86+ message : Vec < u8 > ,
87+ ) -> Vec < u8 > {
88+ match key_type {
89+ KeyType :: ChainKey => {
90+ let res: Result < ( SignWithSchnorrResult , ) , _ > = ic_cdk:: api:: call:: call_with_payment (
91+ Principal :: management_canister ( ) ,
92+ "sign_with_schnorr" ,
93+ ( SignWithSchnorrArgs {
94+ message,
95+ derivation_path : derivation_path
96+ . iter ( )
97+ . map ( |p| p. clone ( ) . into_vec ( ) )
98+ . collect ( ) ,
99+ key_id : SchnorrKeyId {
100+ name : key_name,
101+ algorithm : SchnorrAlgorithm :: Ed25519 ,
102+ } ,
103+ } , ) ,
104+ // https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#fees-for-the-t-schnorr-production-key
105+ // 26_153_846_153,
106+ ECDSA_SIGN_COST as u64 ,
107+ )
108+ . await ;
109+
110+ res. unwrap ( ) . 0 . signature
111+ }
112+ KeyType :: Native ( seed) => {
113+ let derivation_path = derivation_path_ed25519 ( & ic_cdk:: api:: id ( ) , & derivation_path) ;
114+ sign_with_native_ed25519 (
115+ & Seed :: new ( seed. as_slice ( ) . try_into ( ) . unwrap ( ) ) ,
116+ derivation_path,
117+ ByteBuf :: from ( message) ,
118+ )
119+ }
120+ }
121+ }
122+
123+ fn sign_with_native_ed25519 (
124+ seed : & Seed ,
125+ derivation_path : ic_crypto_ed25519:: DerivationPath ,
126+ message : ByteBuf ,
127+ ) -> Vec < u8 > {
128+ let seed_32_bytes =
129+ <[ u8 ; 32 ] >:: try_from ( & seed. as_bytes ( ) [ 0 ..32 ] ) . expect ( "seed should be >= 32 bytes" ) ;
130+ let master_secret = ic_crypto_ed25519:: PrivateKey :: deserialize_raw_32 ( & seed_32_bytes) ;
131+ let ( derived_secret, _chain_code) = master_secret. derive_subkey ( & derivation_path) ;
132+ derived_secret. sign_message ( & message) . to_vec ( )
133+ }
134+
135+ pub fn sha256 ( input : & [ u8 ] ) -> [ u8 ; 32 ] {
136+ let mut hasher = sha2:: Sha256 :: new ( ) ;
137+ hasher. update ( input) ;
138+ hasher. finalize ( ) . into ( )
139+ }
140+
141+ pub fn hash_with_sha256 ( input : & str ) -> String {
142+ let value = sha256 ( input. as_bytes ( ) ) ;
143+ hex:: encode ( value)
144+ }
145+
146+ #[ cfg( test) ]
147+ mod tests {
148+ use super :: * ;
149+ use crate :: types:: Pubkey ;
150+
151+ #[ test]
152+ fn test_sign_and_verify_native_schnorr_ed25519 ( ) {
153+ use ed25519_dalek:: { Signature , Verifier , VerifyingKey } ;
154+
155+ // Setup for signing
156+ let test_seed = [ 1u8 ; 64 ] ;
157+ // Example derivation path for signing
158+ let derivation_path = [ vec ! [ 1u8 ; 4 ] ]
159+ . iter ( )
160+ . map ( |v| ByteBuf :: from ( v. clone ( ) ) )
161+ . collect ( ) ;
162+ let derivation_path = derivation_path_ed25519 ( & Principal :: anonymous ( ) , & derivation_path) ;
163+
164+ let message = b"Test message" ;
165+
166+ // Call the sign function
167+ let sign_reply = sign_with_native_ed25519 (
168+ & Seed :: new ( test_seed) ,
169+ derivation_path. clone ( ) ,
170+ ByteBuf :: from ( message. to_vec ( ) ) ,
171+ ) ;
172+
173+ // Setup for verification
174+ let signature = Signature :: from_slice ( & sign_reply) . expect ( "Invalid signature format" ) ;
175+ println ! ( "signature: {:?}" , signature) ;
176+ let public_key_reply = native_public_key_ed25519 ( Seed :: new ( test_seed) , derivation_path) ;
177+ let pk = Pubkey :: try_from ( public_key_reply. to_owned ( ) . as_slice ( ) )
178+ . map_err ( |e| e. to_string ( ) )
179+ . unwrap ( ) ;
180+ println ! ( "public_key: {:?}" , pk. to_string( ) ) ;
181+
182+ let raw_public_key = public_key_reply. as_slice ( ) ;
183+ assert_eq ! ( raw_public_key. len( ) , 32 ) ;
184+ let mut public_key = [ 0u8 ; 32 ] ;
185+ public_key. copy_from_slice ( raw_public_key) ;
186+
187+ let public_key = VerifyingKey :: from_bytes ( & public_key) . unwrap ( ) ;
188+
189+ // Verify the signature
190+ assert ! ( public_key. verify( message, & signature) . is_ok( ) ) ;
191+ }
192+ }
0 commit comments