From d40f2f07b5ef13742bb848c274bed2103a9e06be Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Mon, 29 Jul 2024 13:12:37 +0800 Subject: [PATCH] docs: add docs for `starknet-signers` --- starknet-signers/src/key_pair.rs | 14 ++++++++++++++ starknet-signers/src/ledger.rs | 13 ++++++++++++- starknet-signers/src/lib.rs | 8 ++++++++ starknet-signers/src/local_wallet.rs | 5 +++++ starknet-signers/src/signer.rs | 14 ++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/starknet-signers/src/key_pair.rs b/starknet-signers/src/key_pair.rs index 65047f20..2b24798e 100644 --- a/starknet-signers/src/key_pair.rs +++ b/starknet-signers/src/key_pair.rs @@ -6,23 +6,29 @@ use starknet_core::{ }; use starknet_crypto::get_public_key; +/// A ECDSA signing (private) key on the STARK curve. #[derive(Debug, Clone)] pub struct SigningKey { secret_scalar: Felt, } +/// A ECDSA verifying (public) key on the STARK curve. #[derive(Debug, Clone)] pub struct VerifyingKey { scalar: Felt, } +/// Errors using an encrypted JSON keystore. #[cfg(not(target_arch = "wasm32"))] #[derive(Debug, thiserror::Error)] pub enum KeystoreError { + /// The file path is invalid. #[error("invalid path")] InvalidPath, + /// The decrypted secret scalar is not a valid private key. #[error("invalid decrypted secret scalar")] InvalidScalar, + /// Upstream `eth-keystore` error propagated. #[error(transparent)] Inner(eth_keystore::KeystoreError), } @@ -47,6 +53,7 @@ impl SigningKey { Self { secret_scalar } } + /// Constructs [`SigningKey`] directly from a secret scalar. pub const fn from_secret_scalar(secret_scalar: Felt) -> Self { Self { secret_scalar } } @@ -92,28 +99,35 @@ impl SigningKey { Ok(()) } + /// Gets the secret scalar in the signing key. pub const fn secret_scalar(&self) -> Felt { self.secret_scalar } + /// Derives the verifying (public) key that corresponds to the signing key. pub fn verifying_key(&self) -> VerifyingKey { VerifyingKey::from_scalar(get_public_key(&self.secret_scalar)) } + /// Signs a raw hash using ECDSA for a signature. pub fn sign(&self, hash: &Felt) -> Result { ecdsa_sign(&self.secret_scalar, hash).map(|sig| sig.into()) } } impl VerifyingKey { + /// Constructs [`VerifyingKey`] directly from a scalar. pub const fn from_scalar(scalar: Felt) -> Self { Self { scalar } } + /// Gets the scalar in the verifying key. pub const fn scalar(&self) -> Felt { self.scalar } + /// Verifies that an ECDSA signature is valid for the verifying key against a certain message + /// hash. pub fn verify(&self, hash: &Felt, signature: &Signature) -> Result { ecdsa_verify(&self.scalar, hash, signature) } diff --git a/starknet-signers/src/ledger.rs b/starknet-signers/src/ledger.rs index e86ae90d..fd139b06 100644 --- a/starknet-signers/src/ledger.rs +++ b/starknet-signers/src/ledger.rs @@ -37,18 +37,29 @@ pub struct LedgerStarknetApp { transport: Ledger, } +/// Errors using the Ledger hardware wallet. #[derive(Debug, thiserror::Error)] pub enum LedgerError { + /// The HD wallet derivation path is malformed or does not conform to EIP-2645. #[error("derivation path is empty, not prefixed with m/2645', or is not 6-level long")] InvalidDerivationPath, + /// Error communicating with the Ledger hardware device. #[error(transparent)] TransportError(coins_ledger::LedgerError), + /// An unknown response code is returned from the device. #[error("unknown response code from Ledger: {0}")] UnknownResponseCode(u16), + /// The response code returned from the device does not indicate success. #[error("failed Ledger request: {0}")] UnsuccessfulRequest(APDUResponseCodes), + /// The response has an unexpected size. #[error("unexpected response length - expected: {expected}; actual: {actual}")] - UnexpectedResponseLength { expected: usize, actual: usize }, + UnexpectedResponseLength { + /// The expected response size. + expected: usize, + /// The actual response size. + actual: usize, + }, } /// The `GetPubKey` Ledger command. diff --git a/starknet-signers/src/lib.rs b/starknet-signers/src/lib.rs index 50903fab..59f9d292 100644 --- a/starknet-signers/src/lib.rs +++ b/starknet-signers/src/lib.rs @@ -1,3 +1,7 @@ +//! Starknet signer interface and common implementations. + +#![deny(missing_docs)] + mod key_pair; pub use key_pair::{SigningKey, VerifyingKey}; @@ -7,13 +11,17 @@ pub use key_pair::KeystoreError; mod signer; pub use signer::Signer; +/// Module containing types related to the use of a simple in-memory signer. pub mod local_wallet; pub use local_wallet::LocalWallet; +/// Module containing types related to the Ledger hardware wallet. #[cfg(feature = "ledger")] pub mod ledger; #[cfg(feature = "ledger")] pub use ledger::{DerivationPath, LedgerError, LedgerSigner}; +/// An error type that indicates an error cannot possibly occur. Used as placeholder where +/// [`Result`] is expected. #[derive(Debug, thiserror::Error)] pub enum Infallible {} diff --git a/starknet-signers/src/local_wallet.rs b/starknet-signers/src/local_wallet.rs index 2c898da0..0ad14c61 100644 --- a/starknet-signers/src/local_wallet.rs +++ b/starknet-signers/src/local_wallet.rs @@ -6,18 +6,23 @@ use starknet_core::{ types::Felt, }; +/// A signer that simply holds the signing (private) key in memory for performing cryptographic +/// operations. It's recommended to use hardware-based signers for use cases involving real value. #[derive(Debug, Clone)] pub struct LocalWallet { private_key: SigningKey, } +/// Errors using [`LocalWallet`]. #[derive(Debug, thiserror::Error)] pub enum SignError { + /// ECDSA signature error. #[error(transparent)] EcdsaSignError(EcdsaSignError), } impl LocalWallet { + /// Constructs [`LocalWallet`] from a [`SigningKey`]. pub fn from_signing_key(key: SigningKey) -> Self { key.into() } diff --git a/starknet-signers/src/signer.rs b/starknet-signers/src/signer.rs index 429efede..2a7d3a46 100644 --- a/starknet-signers/src/signer.rs +++ b/starknet-signers/src/signer.rs @@ -5,15 +5,29 @@ use auto_impl::auto_impl; use starknet_core::{crypto::Signature, types::Felt}; use std::error::Error; +/// Any signer that can provide a public key as [`Felt`], and sign a raw hash for a signature +/// encoded as [`Vec`]. #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[auto_impl(&, Box, Arc)] pub trait Signer { + /// Possible errors for calling [`get_public_key`](fn.get_public_key). type GetPublicKeyError: Error + Send + Sync; + /// Possible errors for calling [`sign`](fn.sign). type SignError: Error + Send + Sync; + /// Retrieves the verifying (public) key from the signer. async fn get_public_key(&self) -> Result; + /// Requests an ECDSA signature for a message hash. + /// + /// Signing a raw hash is known as "blind signing". For interactive signers (e.g. hardware + /// wallets) that can theoretically provide better security properties via "clear signing", + /// using blind signing is bad practice. + /// + /// However, as of this writing, no actual interactive signer implementation offers clear + /// signing. When this changes in the future, this trait shall be altered to allow such clear + /// signing capabilities. async fn sign_hash(&self, hash: &Felt) -> Result; /// Whether the underlying signer implementation is interactive, such as a hardware wallet.