lox_library/
migration_table.rs1use curve25519_dalek::ristretto::CompressedRistretto;
12#[cfg(feature = "bridgeauth")]
13use curve25519_dalek::ristretto::RistrettoBasepointTable;
14use curve25519_dalek::ristretto::RistrettoPoint;
15use curve25519_dalek::scalar::Scalar;
16
17use sha2::Digest;
18use sha2::Sha256;
19
20use aes_gcm::aead::{generic_array::GenericArray, Aead};
21use aes_gcm::{Aes128Gcm, KeyInit};
22#[cfg(feature = "bridgeauth")]
23use rand::RngCore;
24
25use std::collections::HashMap;
26
27#[cfg(feature = "bridgeauth")]
28use serde::{Deserialize, Serialize};
29
30#[cfg(feature = "bridgeauth")]
31use super::bridge_table;
32use super::cred::Migration;
33#[cfg(feature = "bridgeauth")]
34use super::IssuerPrivKey;
35#[cfg(feature = "bridgeauth")]
36use super::CMZ_B_TABLE;
37
38pub const MIGRATION_BYTES: usize = 96;
41
42pub const ENC_MIGRATION_BYTES: usize = MIGRATION_BYTES + 12 + 16;
44
45pub enum MigrationType {
51 TrustUpgrade,
52 Blockage,
53}
54
55impl From<MigrationType> for Scalar {
56 fn from(m: MigrationType) -> Self {
59 match m {
60 MigrationType::TrustUpgrade => 0u32,
61 MigrationType::Blockage => 1u32,
62 }
63 .into()
64 }
65}
66
67#[derive(Default, Debug, Serialize, Deserialize)]
69#[cfg(feature = "bridgeauth")]
70pub struct MigrationTable {
71 pub table: HashMap<u32, u32>,
72 pub migration_type: Scalar,
73}
74
75#[cfg(feature = "bridgeauth")]
88pub fn encrypt_cred(
89 id: &Scalar,
90 from_bucket: &Scalar,
91 to_bucket: &Scalar,
92 migration_type: &Scalar,
93 Pktable: &RistrettoBasepointTable,
94 migration_priv: &IssuerPrivKey,
95 migrationkey_priv: &IssuerPrivKey,
96) -> ([u8; 16], [u8; ENC_MIGRATION_BYTES]) {
97 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
98
99 let mut rng = rand::thread_rng();
100
101 let Qk = &(migrationkey_priv.x[0]
103 + migrationkey_priv.x[1] * id
104 + migrationkey_priv.x[2] * from_bucket)
105 * Pktable;
106
107 let b = Scalar::random(&mut rng);
109 let P = &b * Btable;
110 let Q = &(b
111 * (migration_priv.x[0]
112 + migration_priv.x[1] * id
113 + migration_priv.x[2] * from_bucket
114 + migration_priv.x[3] * to_bucket
115 + migration_priv.x[4] * migration_type))
116 * Btable;
117
118 let mut credbytes: [u8; MIGRATION_BYTES] = [0; MIGRATION_BYTES];
120 credbytes[0..32].copy_from_slice(to_bucket.as_bytes());
121 credbytes[32..64].copy_from_slice(P.compress().as_bytes());
122 credbytes[64..].copy_from_slice(Q.compress().as_bytes());
123
124 let mut noncebytes: [u8; 12] = [0; 12];
126 rng.fill_bytes(&mut noncebytes);
127 let nonce = GenericArray::from_slice(&noncebytes);
128
129 let mut hasher = Sha256::new();
131 hasher.update(id.as_bytes());
132 hasher.update(from_bucket.as_bytes());
133 hasher.update(Qk.compress().as_bytes());
134 let fullhash = hasher.finalize();
135
136 let aeskey = GenericArray::from_slice(&fullhash[16..]);
138 let cipher = Aes128Gcm::new(aeskey);
140 let ciphertext: Vec<u8> = cipher.encrypt(nonce, credbytes.as_ref()).unwrap();
141 let mut enccredbytes: [u8; ENC_MIGRATION_BYTES] = [0; ENC_MIGRATION_BYTES];
142 enccredbytes[..12].copy_from_slice(&noncebytes);
143 enccredbytes[12..].copy_from_slice(ciphertext.as_slice());
144
145 let mut label: [u8; 16] = [0; 16];
147 label[..].copy_from_slice(&fullhash[..16]);
148
149 (label, enccredbytes)
150}
151
152#[cfg(feature = "bridgeauth")]
160#[allow(clippy::too_many_arguments)]
161pub fn encrypt_cred_ids(
162 id: &Scalar,
163 from_id: u32,
164 to_id: u32,
165 migration_type: &Scalar,
166 bridgetable: &bridge_table::BridgeTable,
167 Pktable: &RistrettoBasepointTable,
168 migration_priv: &IssuerPrivKey,
169 migrationkey_priv: &IssuerPrivKey,
170) -> Option<([u8; 16], [u8; ENC_MIGRATION_BYTES])> {
171 let fromkey = bridgetable.keys.get(&from_id)?;
173 let tokey = bridgetable.keys.get(&to_id)?;
174 Some(encrypt_cred(
175 id,
176 &bridge_table::to_scalar(from_id, fromkey),
177 &bridge_table::to_scalar(to_id, tokey),
178 migration_type,
179 Pktable,
180 migration_priv,
181 migrationkey_priv,
182 ))
183}
184
185#[cfg(feature = "bridgeauth")]
186impl MigrationTable {
187 pub fn new(table_type: MigrationType) -> Self {
189 Self {
190 table: Default::default(),
191 migration_type: table_type.into(),
192 }
193 }
194
195 pub fn encrypt_table(
199 &self,
200 id: &Scalar,
201 bridgetable: &bridge_table::BridgeTable,
202 Pktable: &RistrettoBasepointTable,
203 migration_priv: &IssuerPrivKey,
204 migrationkey_priv: &IssuerPrivKey,
205 ) -> HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]> {
206 self.table
207 .iter()
208 .filter_map(|(from_id, to_id)| {
209 encrypt_cred_ids(
210 id,
211 *from_id,
212 *to_id,
213 &self.migration_type,
214 bridgetable,
215 Pktable,
216 migration_priv,
217 migrationkey_priv,
218 )
219 })
220 .collect()
221 }
222}
223
224pub fn decrypt_cred(
229 Qk: &RistrettoPoint,
230 lox_id: &Scalar,
231 from_bucket: &Scalar,
232 migration_type: MigrationType,
233 enc_migration_table: &HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
234) -> Option<Migration> {
235 let mut hasher = Sha256::new();
237 hasher.update(lox_id.as_bytes());
238 hasher.update(from_bucket.as_bytes());
239 hasher.update(Qk.compress().as_bytes());
240 let fullhash = hasher.finalize();
241
242 let mut label: [u8; 16] = [0; 16];
244 label[..].copy_from_slice(&fullhash[..16]);
245
246 let ciphertext = enc_migration_table.get(&label)?;
248
249 let aeskey = GenericArray::from_slice(&fullhash[16..]);
251
252 let nonce = GenericArray::from_slice(&ciphertext[..12]);
254 let cipher = Aes128Gcm::new(aeskey);
255 let plaintext: Vec<u8> = match cipher.decrypt(nonce, ciphertext[12..].as_ref()) {
256 Ok(v) => v,
257 Err(_) => return None,
258 };
259 let plaintextbytes = plaintext.as_slice();
260 let mut to_bucket_bytes: [u8; 32] = [0; 32];
261 to_bucket_bytes.copy_from_slice(&plaintextbytes[..32]);
262 let to_bucket = Scalar::from_bytes_mod_order(to_bucket_bytes);
263 let P = CompressedRistretto::from_slice(&plaintextbytes[32..64])
264 .expect("Unable to extract P from bucket")
265 .decompress()?;
266 let Q = CompressedRistretto::from_slice(&plaintextbytes[64..])
267 .expect("Unable to extract Q from bucket")
268 .decompress()?;
269
270 Some(Migration {
271 P,
272 Q,
273 lox_id: *lox_id,
274 from_bucket: *from_bucket,
275 to_bucket,
276 migration_type: migration_type.into(),
277 })
278}