Skip to content

Commit

Permalink
feat: Add Schnorr signing API (#518)
Browse files Browse the repository at this point in the history
Co-authored-by: Severin Siffert <[email protected]>
  • Loading branch information
lwshang and sesi200 authored Sep 17, 2024
1 parent 97c4e19 commit 19bfaf3
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 3 deletions.
34 changes: 34 additions & 0 deletions examples/management_canister/src/caller/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,40 @@ mod ecdsa {
}
}

mod schnorr {
use super::*;
use ic_cdk::api::management_canister::schnorr::*;

#[update]
async fn execute_schnorr_methods() {
let key_id = SchnorrKeyId {
algorithm: SchnorrAlgorithm::Bip340secp256k1,
name: "dfx_test_key".to_string(),
};
let derivation_path = vec![];
let arg = SchnorrPublicKeyArgument {
canister_id: None,
derivation_path: derivation_path.clone(),
key_id: key_id.clone(),
};
let SchnorrPublicKeyResponse {
public_key,
chain_code,
} = schnorr_public_key(arg).await.unwrap().0;
assert_eq!(public_key.len(), 33);
assert_eq!(chain_code.len(), 32);

let message = "hello world".into();
let arg = SignWithSchnorrArgument {
message,
derivation_path,
key_id,
};
let SignWithSchnorrResponse { signature } = sign_with_schnorr(arg).await.unwrap().0;
assert_eq!(signature.len(), 64);
}
}

mod bitcoin {
use super::*;
use ic_cdk::api::{call::RejectionCode, management_canister::bitcoin::*};
Expand Down
7 changes: 7 additions & 0 deletions examples/management_canister/tests/basic.bats
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ teardown() {
assert_success
}

@test "schnorr methods succeed" {
dfx start --clean --background
dfx deploy
run dfx canister call caller execute_schnorr_methods
assert_success
}

@test "bitcoin methods succeed" {
bitcoind -regtest -daemonwait

Expand Down
8 changes: 7 additions & 1 deletion src/ic-cdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

* Add `AllowedViewers` to `LogVisibility` enum.
### Changed

- Add `AllowedViewers` variant to `LogVisibility` enum. (#512)

### Added

- Support Threshold Schnorr signing management canister API. (#518)

## [0.16.0] - 2024-08-27

Expand Down
4 changes: 2 additions & 2 deletions src/ic-cdk/src/api/management_canister/ecdsa/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! The ECDSA API.
//! Threshold ECDSA signing API.
use crate::api::call::{call, call_with_payment128, CallResult};
use candid::Principal;
Expand All @@ -23,7 +23,7 @@ pub async fn ecdsa_public_key(
///
/// This call requires cycles payment.
/// This method handles the cycles cost under the hood.
/// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details.
/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details.
pub async fn sign_with_ecdsa(arg: SignWithEcdsaArgument) -> CallResult<(SignWithEcdsaResponse,)> {
call_with_payment128(
Principal::management_canister(),
Expand Down
1 change: 1 addition & 0 deletions src/ic-cdk/src/api/management_canister/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ pub mod ecdsa;
pub mod http_request;
pub mod main;
pub mod provisional;
pub mod schnorr;
43 changes: 43 additions & 0 deletions src/ic-cdk/src/api/management_canister/schnorr/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Threshold Schnorr signing API.
use crate::api::call::{call, call_with_payment128, CallResult};
use candid::Principal;

mod types;
pub use types::*;

// Source: https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#fees-for-the-t-schnorr-production-key
const SIGN_WITH_SCHNORR_FEE: u128 = 26_153_846_153;

/// Return a SEC1 encoded Schnorr public key for the given canister using the given derivation path.
///
/// See [IC method `schnorr_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-schnorr_public_key).
pub async fn schnorr_public_key(
arg: SchnorrPublicKeyArgument,
) -> CallResult<(SchnorrPublicKeyResponse,)> {
call(
Principal::management_canister(),
"schnorr_public_key",
(arg,),
)
.await
}

/// Return a new Schnorr signature of the given message that can be separately verified against a derived Schnorr public key.
///
/// See [IC method `sign_with_schnorr`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_schnorr).
///
/// This call requires cycles payment.
/// This method handles the cycles cost under the hood.
/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details.
pub async fn sign_with_schnorr(
arg: SignWithSchnorrArgument,
) -> CallResult<(SignWithSchnorrResponse,)> {
call_with_payment128(
Principal::management_canister(),
"sign_with_schnorr",
(arg,),
SIGN_WITH_SCHNORR_FEE,
)
.await
}
86 changes: 86 additions & 0 deletions src/ic-cdk/src/api/management_canister/schnorr/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use candid::CandidType;
use serde::{Deserialize, Serialize};

use super::super::main::CanisterId;

/// Argument Type of [schnorr_public_key](super::schnorr_public_key).
#[derive(
CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct SchnorrPublicKeyArgument {
/// Canister id, default to the canister id of the caller if None.
pub canister_id: Option<CanisterId>,
/// A vector of variable length byte strings.
pub derivation_path: Vec<Vec<u8>>,
/// See [SchnorrKeyId].
pub key_id: SchnorrKeyId,
}

/// Response Type of [schnorr_public_key](super::schnorr_public_key).
#[derive(
CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct SchnorrPublicKeyResponse {
/// An Schnorr public key encoded in SEC1 compressed form.
pub public_key: Vec<u8>,
/// Can be used to deterministically derive child keys of the public_key.
pub chain_code: Vec<u8>,
}

/// Argument Type of [sign_with_schnorr](super::sign_with_schnorr).
#[derive(
CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct SignWithSchnorrArgument {
/// Message to be signed.
pub message: Vec<u8>,
/// A vector of variable length byte strings.
pub derivation_path: Vec<Vec<u8>>,
/// See [SchnorrKeyId].
pub key_id: SchnorrKeyId,
}

/// Response Type of [sign_with_schnorr](super::sign_with_schnorr).
#[derive(
CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct SignWithSchnorrResponse {
/// The encoding of the signature depends on the key ID's algorithm.
pub signature: Vec<u8>,
}

/// Schnorr KeyId.
#[derive(
CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
)]
pub struct SchnorrKeyId {
/// See [SchnorrAlgorithm].
pub algorithm: SchnorrAlgorithm,
/// Name.
pub name: String,
}

/// Schnorr Algorithm.
#[derive(
CandidType,
Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Clone,
Copy,
Default,
)]
pub enum SchnorrAlgorithm {
/// BIP-340 secp256k1.
#[serde(rename = "bip340secp256k1")]
#[default]
Bip340secp256k1,
/// ed25519.
#[serde(rename = "ed25519")]
Ed25519,
}

0 comments on commit 19bfaf3

Please sign in to comment.