1use std::sync::Arc;
4
5use aztec_core::error::Error;
6use aztec_core::types::{AztecAddress, Fr, GrumpkinScalar, PublicKeys};
7use aztec_crypto::keys::{
8 compute_app_nullifier_hiding_key, compute_app_secret_key, compute_ovsk_app, derive_keys,
9 DerivedKeys, KeyType,
10};
11
12use super::kv::KvStore;
13
14pub struct KeyStore {
16 kv: Arc<dyn KvStore>,
17}
18
19impl KeyStore {
20 pub fn new(kv: Arc<dyn KvStore>) -> Self {
21 Self { kv }
22 }
23
24 pub async fn add_account(&self, secret_key: &Fr) -> Result<DerivedKeys, Error> {
26 let derived = derive_keys(secret_key);
27 let pk_hash = derived.public_keys.hash();
28 let key = account_key(&pk_hash);
29 let value = secret_key.to_be_bytes();
30 self.kv.put(&key, &value).await?;
31 Ok(derived)
32 }
33
34 pub async fn get_secret_key(&self, pk_hash: &Fr) -> Result<Option<Fr>, Error> {
36 let key = account_key(pk_hash);
37 match self.kv.get(&key).await? {
38 Some(bytes) => {
39 let mut arr = [0u8; 32];
40 if bytes.len() == 32 {
41 arr.copy_from_slice(&bytes);
42 Ok(Some(Fr::from(arr)))
43 } else {
44 Err(Error::InvalidData("invalid secret key length".into()))
45 }
46 }
47 None => Ok(None),
48 }
49 }
50
51 pub async fn get_master_nullifier_hiding_key(
53 &self,
54 pk_hash: &Fr,
55 ) -> Result<Option<GrumpkinScalar>, Error> {
56 match self.get_secret_key(pk_hash).await? {
57 Some(sk) => Ok(Some(derive_keys(&sk).master_nullifier_hiding_key)),
58 None => Ok(None),
59 }
60 }
61
62 pub async fn get_master_incoming_viewing_secret_key(
64 &self,
65 pk_hash: &Fr,
66 ) -> Result<Option<GrumpkinScalar>, Error> {
67 match self.get_secret_key(pk_hash).await? {
68 Some(sk) => Ok(Some(derive_keys(&sk).master_incoming_viewing_secret_key)),
69 None => Ok(None),
70 }
71 }
72
73 pub async fn get_master_outgoing_viewing_secret_key(
75 &self,
76 pk_hash: &Fr,
77 ) -> Result<Option<GrumpkinScalar>, Error> {
78 match self.get_secret_key(pk_hash).await? {
79 Some(sk) => Ok(Some(derive_keys(&sk).master_outgoing_viewing_secret_key)),
80 None => Ok(None),
81 }
82 }
83
84 pub async fn get_master_tagging_secret_key(
86 &self,
87 pk_hash: &Fr,
88 ) -> Result<Option<GrumpkinScalar>, Error> {
89 match self.get_secret_key(pk_hash).await? {
90 Some(sk) => Ok(Some(derive_keys(&sk).master_tagging_secret_key)),
91 None => Ok(None),
92 }
93 }
94
95 pub async fn get_public_keys(&self, pk_hash: &Fr) -> Result<Option<PublicKeys>, Error> {
97 match self.get_secret_key(pk_hash).await? {
98 Some(sk) => Ok(Some(derive_keys(&sk).public_keys)),
99 None => Ok(None),
100 }
101 }
102
103 pub async fn get_app_nullifier_hiding_key(
105 &self,
106 pk_hash: &Fr,
107 app: &AztecAddress,
108 ) -> Result<Option<Fr>, Error> {
109 match self.get_master_nullifier_hiding_key(pk_hash).await? {
110 Some(nhk_m) => Ok(Some(compute_app_nullifier_hiding_key(&nhk_m, app))),
111 None => Ok(None),
112 }
113 }
114
115 pub async fn get_key_validation_request(
120 &self,
121 pk_m_hash: &Fr,
122 app: &AztecAddress,
123 ) -> Result<Option<(aztec_core::types::Point, Fr)>, Error> {
124 use aztec_core::hash::poseidon2_hash;
125
126 let accounts = self.get_accounts().await?;
127 for account_pk_hash in &accounts {
128 let Some(sk) = self.get_secret_key(account_pk_hash).await? else {
129 continue;
130 };
131 let derived = derive_keys(&sk);
132 let pk = &derived.public_keys;
133 let keys_and_types = [
134 (
135 &pk.master_nullifier_public_key,
136 &derived.master_nullifier_hiding_key,
137 KeyType::Nullifier,
138 ),
139 (
140 &pk.master_incoming_viewing_public_key,
141 &derived.master_incoming_viewing_secret_key,
142 KeyType::IncomingViewing,
143 ),
144 (
145 &pk.master_outgoing_viewing_public_key,
146 &derived.master_outgoing_viewing_secret_key,
147 KeyType::OutgoingViewing,
148 ),
149 (
150 &pk.master_tagging_public_key,
151 &derived.master_tagging_secret_key,
152 KeyType::Tagging,
153 ),
154 ];
155
156 for (pk_m, sk_m, key_type) in &keys_and_types {
157 let hash = poseidon2_hash(&[pk_m.x, pk_m.y, Fr::from(pk_m.is_infinite)]);
158 if hash == *pk_m_hash {
159 let sk_app = compute_app_secret_key(sk_m, app, *key_type);
160 return Ok(Some(((*pk_m).clone(), sk_app)));
161 }
162 }
163 }
164 Ok(None)
165 }
166
167 pub async fn get_app_secret_key(
169 &self,
170 pk_hash: &Fr,
171 app: &AztecAddress,
172 key_type: KeyType,
173 ) -> Result<Option<Fr>, Error> {
174 let sk = match self.get_secret_key(pk_hash).await? {
175 Some(sk) => sk,
176 None => return Ok(None),
177 };
178 let derived = derive_keys(&sk);
179 let master_key = match key_type {
180 KeyType::Nullifier => &derived.master_nullifier_hiding_key,
181 KeyType::IncomingViewing => &derived.master_incoming_viewing_secret_key,
182 KeyType::OutgoingViewing => &derived.master_outgoing_viewing_secret_key,
183 KeyType::Tagging => &derived.master_tagging_secret_key,
184 };
185 Ok(Some(compute_app_secret_key(master_key, app, key_type)))
186 }
187
188 pub async fn get_app_ovsk(
190 &self,
191 pk_hash: &Fr,
192 app: &AztecAddress,
193 ) -> Result<Option<GrumpkinScalar>, Error> {
194 match self.get_master_outgoing_viewing_secret_key(pk_hash).await? {
195 Some(ovsk_m) => Ok(Some(compute_ovsk_app(&ovsk_m, app))),
196 None => Ok(None),
197 }
198 }
199
200 pub async fn get_accounts(&self) -> Result<Vec<Fr>, Error> {
202 let entries = self.kv.list_prefix(b"key:account:").await?;
203 entries
204 .into_iter()
205 .map(|(k, _)| {
206 let key_str = String::from_utf8_lossy(&k);
207 let hex_part = key_str
208 .strip_prefix("key:account:")
209 .ok_or_else(|| Error::InvalidData("invalid key prefix".into()))?;
210 Fr::from_hex(hex_part)
211 })
212 .collect()
213 }
214}
215
216fn account_key(pk_hash: &Fr) -> Vec<u8> {
217 format!("key:account:{pk_hash}").into_bytes()
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use crate::stores::InMemoryKvStore;
224
225 #[tokio::test]
226 async fn add_and_retrieve_account() {
227 let kv = Arc::new(InMemoryKvStore::new());
228 let store = KeyStore::new(kv);
229 let sk = Fr::from(8923u64);
230
231 let derived = store.add_account(&sk).await.unwrap();
232 let pk_hash = derived.public_keys.hash();
233
234 let retrieved_sk = store.get_secret_key(&pk_hash).await.unwrap().unwrap();
235 assert_eq!(retrieved_sk, sk);
236
237 let public_keys = store.get_public_keys(&pk_hash).await.unwrap().unwrap();
238 assert_eq!(public_keys.hash(), pk_hash);
239 }
240}