iqkms_signing/
lib.rs

1//! iqkms keyring: data structure which stores available keys.
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![doc = include_str!("../README.md")]
5#![doc(
6    html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/iqkms/main/.img/iqkms-sq.svg"
7)]
8#![forbid(unsafe_code)]
9#![warn(
10    clippy::integer_arithmetic,
11    clippy::panic,
12    clippy::panic_in_result_fn,
13    clippy::unwrap_used,
14    missing_docs,
15    rust_2018_idioms,
16    unused_lifetimes,
17    unused_qualifications
18)]
19
20mod error;
21
22pub use crate::error::{Error, Result};
23pub use crypto::{
24    digest::{sha2::Sha256, Digest},
25    rand::{OsRng, RngCore},
26    signature,
27};
28
29use crypto::signature::{ecdsa, hazmat::PrehashSigner};
30use std::collections::BTreeMap as Map;
31
32#[cfg(feature = "ethereum")]
33use types::ethereum;
34
35/// Keys for producing digital signatures.
36#[derive(Default)]
37pub struct KeyRing {
38    /// Signing keys.
39    keys: Map<VerifyingKey, SigningKey>,
40
41    /// Ethereum address index.
42    #[cfg(feature = "ethereum")]
43    eth_index: Map<ethereum::Address, VerifyingKey>,
44}
45
46impl KeyRing {
47    /// Create a new key ring.
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    /// Add a key to the ring.
53    pub fn add(&mut self, signing_key: SigningKey) -> Result<()> {
54        let verifying_key = signing_key.verifying_key();
55
56        #[cfg(feature = "ethereum")]
57        #[allow(irrefutable_let_patterns)]
58        if let VerifyingKey::EcdsaSecp256k1(vk) = &verifying_key {
59            self.eth_index.insert(vk.try_into()?, verifying_key.clone());
60        }
61
62        if self.keys.insert(verifying_key, signing_key).is_some() {
63            Err(Error)
64        } else {
65            Ok(())
66        }
67    }
68
69    /// Find a key by its Ethereum address.
70    #[cfg(feature = "ethereum")]
71    #[cfg_attr(docsrs, doc(cfg(feature = "ethereum")))]
72    pub fn find_by_eth_address(
73        &self,
74        addr: &ethereum::Address,
75    ) -> Result<&ecdsa::secp256k1::SigningKey> {
76        let signing_key = self
77            .eth_index
78            .get(addr)
79            .and_then(|vk| self.keys.get(vk))
80            .ok_or(Error)?;
81
82        match signing_key {
83            SigningKey::EcdsaSecp256k1(key) => Ok(key),
84            #[allow(unreachable_patterns)]
85            _ => Err(Error),
86        }
87    }
88}
89
90/// Signing key types.
91pub enum SigningKey {
92    /// ECDSA/secp256k1
93    #[cfg(feature = "secp256k1")]
94    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
95    EcdsaSecp256k1(ecdsa::secp256k1::SigningKey),
96}
97
98impl SigningKey {
99    /// Generate a random ECDSA/secp256k1 key.
100    // TODO(tarcieri): unified `generate` method with algorithm parameter
101    #[cfg(feature = "secp256k1")]
102    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
103    pub fn generate_secp256k1() -> Self {
104        let mut bytes = [0u8; 32];
105
106        loop {
107            OsRng.fill_bytes(&mut bytes);
108
109            if let Ok(signing_key) = ecdsa::secp256k1::SigningKey::from_bytes(&bytes) {
110                // TODO(tarcieri): zeroize bytes
111                return signing_key.into();
112            }
113        }
114    }
115
116    /// Sign the given message with this key.
117    // TODO(tarcieri): support for customizing hash function used
118    pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
119        self.sign_digest(&Sha256::digest(msg))
120    }
121
122    /// Sign the given prehashed message digest with this key.
123    pub fn sign_digest(&self, msg_digest: &[u8]) -> Result<Vec<u8>> {
124        match self {
125            #[cfg(feature = "secp256k1")]
126            Self::EcdsaSecp256k1(sk) => {
127                PrehashSigner::<ecdsa::secp256k1::Signature>::sign_prehash(sk, msg_digest)
128                    .map(|sig| sig.to_vec())
129                    .map_err(|_| Error)
130            }
131        }
132    }
133
134    /// Get the [`VerifyingKey`] that corresponds to this signing key.
135    pub fn verifying_key(&self) -> VerifyingKey {
136        match self {
137            #[cfg(feature = "secp256k1")]
138            SigningKey::EcdsaSecp256k1(sk) => VerifyingKey::EcdsaSecp256k1(sk.verifying_key()),
139        }
140    }
141}
142
143#[cfg(feature = "secp256k1")]
144#[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
145impl From<ecdsa::secp256k1::SigningKey> for SigningKey {
146    #[inline]
147    fn from(key: ecdsa::secp256k1::SigningKey) -> SigningKey {
148        SigningKey::EcdsaSecp256k1(key)
149    }
150}
151
152/// Verifying key types.
153#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
154pub enum VerifyingKey {
155    /// ECDSA/secp256k1
156    #[cfg(feature = "secp256k1")]
157    #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
158    EcdsaSecp256k1(ecdsa::secp256k1::VerifyingKey),
159}