1#![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#[derive(Default)]
37pub struct KeyRing {
38 keys: Map<VerifyingKey, SigningKey>,
40
41 #[cfg(feature = "ethereum")]
43 eth_index: Map<ethereum::Address, VerifyingKey>,
44}
45
46impl KeyRing {
47 pub fn new() -> Self {
49 Self::default()
50 }
51
52 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 #[cfg(feature = "ethereum")]
71 #[cfg_attr(docsrs, doc(cfg(feature = "ethereum")))]
72 pub fn find_by_eth_address(
73 &self,
74 addr: ðereum::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
90pub enum SigningKey {
92 #[cfg(feature = "secp256k1")]
94 #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
95 EcdsaSecp256k1(ecdsa::secp256k1::SigningKey),
96}
97
98impl SigningKey {
99 #[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 return signing_key.into();
112 }
113 }
114 }
115
116 pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
119 self.sign_digest(&Sha256::digest(msg))
120 }
121
122 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 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#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
154pub enum VerifyingKey {
155 #[cfg(feature = "secp256k1")]
157 #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
158 EcdsaSecp256k1(ecdsa::secp256k1::VerifyingKey),
159}