Skip to main content

hiero_sdk/key/
key.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use hiero_sdk_proto::services;
4
5use crate::contract::DelegateContractId;
6use crate::{
7    ContractId,
8    Error,
9    FromProtobuf,
10    KeyList,
11    PublicKey,
12    ToProtobuf,
13};
14
15/// Any method that can be used to authorize an operation on Hiero.
16#[derive(Eq, PartialEq, Debug, Clone, Hash)]
17#[non_exhaustive]
18pub enum Key {
19    // todo(sr): not happy with any of these (fix before merge)
20    /// A single public key.
21    Single(PublicKey),
22
23    /// A contract ID.
24    ContractId(ContractId),
25
26    /// A delegatable contract ID.
27    DelegateContractId(DelegateContractId),
28
29    /// A key list.
30    KeyList(KeyList),
31}
32
33impl Key {
34    /// Convert `self` to a protobuf-encoded [`Vec<u8>`].
35    #[must_use]
36    pub fn to_bytes(&self) -> Vec<u8> {
37        ToProtobuf::to_bytes(self)
38    }
39}
40
41impl ToProtobuf for Key {
42    type Protobuf = services::Key;
43
44    fn to_protobuf(&self) -> Self::Protobuf {
45        use services::key::Key::*;
46
47        services::Key {
48            key: Some(match self {
49                Self::Single(key) => {
50                    let bytes = key.to_bytes_raw();
51
52                    match key.kind() {
53                        crate::key::KeyKind::Ed25519 => Ed25519(bytes),
54                        crate::key::KeyKind::Ecdsa => EcdsaSecp256k1(bytes),
55                    }
56                }
57
58                Self::ContractId(id) => ContractId(id.to_protobuf()),
59                Self::DelegateContractId(id) => DelegatableContractId(id.to_protobuf()),
60                // `KeyList`s are special and can be both a key list and a threshold key.
61                Self::KeyList(key) => key.to_protobuf_key(),
62            }),
63        }
64    }
65}
66
67impl From<PublicKey> for Key {
68    fn from(key: PublicKey) -> Self {
69        Self::Single(key)
70    }
71}
72
73impl From<ContractId> for Key {
74    fn from(id: ContractId) -> Self {
75        Self::ContractId(id)
76    }
77}
78
79impl From<KeyList> for Key {
80    fn from(value: KeyList) -> Self {
81        Self::KeyList(value)
82    }
83}
84
85impl FromProtobuf<services::Key> for Key {
86    fn from_protobuf(pb: services::Key) -> crate::Result<Self>
87    where
88        Self: Sized,
89    {
90        use services::key::Key::*;
91
92        match pb.key {
93            Some(Ed25519(bytes)) => Ok(Self::Single(PublicKey::from_bytes_ed25519(&bytes)?)),
94            Some(ContractId(id)) => Ok(Self::ContractId(crate::ContractId::from_protobuf(id)?)),
95            Some(DelegatableContractId(id)) => {
96                Ok(Self::DelegateContractId(crate::DelegateContractId::from_protobuf(id)?))
97            }
98            Some(Rsa3072(_)) => {
99                Err(Error::from_protobuf("unexpected unsupported RSA-3072 key in Key"))
100            }
101            Some(Ecdsa384(_)) => {
102                Err(Error::from_protobuf("unexpected unsupported ECDSA-384 key in Key"))
103            }
104            Some(ThresholdKey(it)) => Ok(Self::KeyList(crate::KeyList::from_protobuf(it)?)),
105            Some(KeyList(it)) => Ok(Self::KeyList(crate::KeyList::from_protobuf(it)?)),
106            Some(EcdsaSecp256k1(bytes)) => Ok(Self::Single(PublicKey::from_bytes_ecdsa(&bytes)?)),
107            None => Err(Error::from_protobuf("unexpected empty key in Key")),
108        }
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use assert_matches::assert_matches;
115    use hex_literal::hex;
116    use hiero_sdk_proto::services;
117
118    use crate::protobuf::FromProtobuf;
119    use crate::{
120        Key,
121        PublicKey,
122    };
123
124    #[test]
125    fn from_proto_key_ed25519() {
126        const KEY_BYTES: [u8; 32] =
127            hex!("0011223344556677889900112233445566778899001122334455667788990011");
128
129        let key = services::Key { key: Some(services::key::Key::Ed25519(KEY_BYTES.to_vec())) };
130
131        let key = PublicKey::from_protobuf(key).unwrap();
132
133        assert_matches!(key.kind(), crate::key::KeyKind::Ed25519);
134
135        assert_eq!(key.to_bytes_raw(), KEY_BYTES);
136    }
137
138    #[test]
139    fn from_proto_key_ecdsa() {
140        const KEY_BYTES: [u8; 35] =
141            hex!("3a21034e0441201f2bf9c7d9873c2a9dc3fd451f64b7c05e17e4d781d916e3a11dfd99");
142
143        let key = PublicKey::from_alias_bytes(&KEY_BYTES).unwrap().unwrap();
144
145        assert_matches!(key.kind(), crate::key::KeyKind::Ecdsa);
146
147        assert_eq!(Key::from(key).to_bytes(), KEY_BYTES);
148    }
149
150    #[test]
151    fn from_proto_key_key_list() {
152        const KEY_BYTES: [[u8; 32]; 2] = [
153            hex!("0011223344556677889900112233445566778899001122334455667788990011"),
154            hex!("aa11223344556677889900112233445566778899001122334455667788990011"),
155        ];
156
157        let key_list_pb = services::KeyList {
158            keys: KEY_BYTES
159                .iter()
160                .map(|it| services::Key { key: Some(services::key::Key::Ed25519(it.to_vec())) })
161                .collect(),
162        };
163
164        let key_pb = services::Key { key: Some(services::key::Key::KeyList(key_list_pb.clone())) };
165
166        let key = Key::from_protobuf(key_pb).unwrap();
167
168        let key_list = assert_matches!(key, Key::KeyList(it) => it);
169
170        assert_eq!(key_list.len(), KEY_BYTES.len());
171
172        let reencoded =
173            assert_matches!(key_list.to_protobuf_key(), services::key::Key::KeyList(key) => key);
174
175        assert_eq!(reencoded, key_list_pb);
176    }
177
178    #[test]
179    fn from_proto_key_threshold_key() {
180        const KEY_BYTES: [[u8; 32]; 2] = [
181            hex!("0011223344556677889900112233445566778899001122334455667788990011"),
182            hex!("aa11223344556677889900112233445566778899001122334455667788990011"),
183        ];
184
185        let key_list_pb = services::KeyList {
186            keys: KEY_BYTES
187                .iter()
188                .map(|it| services::Key { key: Some(services::key::Key::Ed25519(it.to_vec())) })
189                .collect(),
190        };
191
192        let threshold_key_pb = services::ThresholdKey { threshold: 1, keys: Some(key_list_pb) };
193
194        let key_pb =
195            services::Key { key: Some(services::key::Key::ThresholdKey(threshold_key_pb.clone())) };
196
197        let key = Key::from_protobuf(key_pb).unwrap();
198
199        let threshold_key = assert_matches!(key, Key::KeyList(it) => it);
200
201        assert_eq!(threshold_key.len(), KEY_BYTES.len());
202
203        let reencoded = assert_matches!(threshold_key.to_protobuf_key(), services::key::Key::ThresholdKey(key) => key);
204
205        assert_eq!(reencoded, threshold_key_pb);
206    }
207
208    #[test]
209    fn unsupported_key_fails() {
210        let key = services::Key { key: Some(services::key::Key::Rsa3072(Vec::from([0, 1, 2]))) };
211
212        assert_matches!(Key::from_protobuf(key), Err(crate::Error::FromProtobuf(_)));
213    }
214}