Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add docs for starknet-signers #638

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions starknet-signers/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand All @@ -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 }
}
Expand Down Expand Up @@ -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<Signature, EcdsaSignError> {
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<bool, EcdsaVerifyError> {
ecdsa_verify(&self.scalar, hash, signature)
}
Expand Down
13 changes: 12 additions & 1 deletion starknet-signers/src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions starknet-signers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! Starknet signer interface and common implementations.

#![deny(missing_docs)]

mod key_pair;
pub use key_pair::{SigningKey, VerifyingKey};

Expand All @@ -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 {}
5 changes: 5 additions & 0 deletions starknet-signers/src/local_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
14 changes: 14 additions & 0 deletions starknet-signers/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Felt>`].
#[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<VerifyingKey, Self::GetPublicKeyError>;

/// 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<Signature, Self::SignError>;

/// Whether the underlying signer implementation is interactive, such as a hardware wallet.
Expand Down
Loading