diff --git a/bls/src/types/public_key.rs b/bls/src/types/public_key.rs index ba7a40dbca..b174c7c72b 100644 --- a/bls/src/types/public_key.rs +++ b/bls/src/types/public_key.rs @@ -19,6 +19,9 @@ pub struct PublicKey { } impl PublicKey { + /// Size of the trusted serialization. + /// + /// See [`PublicKey::trusted_deserialize`]. pub const TRUSTED_SERIALIZATION_SIZE: usize = 570; /// Generates a public key from a given point in G2. This function will produce an error if it is given the point at infinity. @@ -62,8 +65,9 @@ impl PublicKey { /// and one bit indicating if it is the "point-at-infinity". pub fn compress(&self) -> CompressedPublicKey { let mut buffer = [0u8; CompressedPublicKey::SIZE]; - CanonicalSerialize::serialize_compressed(&self.public_key.into_affine(), &mut buffer[..]) - .unwrap(); + let affine = self.public_key.into_affine(); + assert_eq!(affine.compressed_size(), buffer.len()); + affine.serialize_compressed(&mut buffer[..]).unwrap(); CompressedPublicKey { public_key: buffer } } @@ -74,6 +78,38 @@ impl PublicKey { let public_key = self.public_key.mul_bigint([x as u64]); PublicKey { public_key } } + + /// Serialization to trusted storage. + /// + /// If in doubt, use the default serialization, or even better, the default + /// serialization of [`LazyPublicKey`](crate::lazy::LazyPublicKey) instead. + /// + /// See [`PublicKey::trusted_deserialize`] for details. + pub fn trusted_serialize(&self) -> [u8; PublicKey::TRUSTED_SERIALIZATION_SIZE] { + let mut result = [0u8; PublicKey::TRUSTED_SERIALIZATION_SIZE]; + assert_eq!(self.public_key.uncompressed_size(), result.len()); + self.public_key + .serialize_uncompressed(&mut result[..]) + .unwrap(); + result + } + + /// Deserialization from trusted storage. + /// + /// **This does not check whether the resulting key is on the curve.** This + /// means that you can only use this serialization when you trust the + /// creator of that serialization completely. + /// + /// If in doubt, use the default serialization, or even better, the default + /// serialization of [`LazyPublicKey`](crate::lazy::LazyPublicKey) instead. + pub fn trusted_deserialize( + serialized: &[u8; PublicKey::TRUSTED_SERIALIZATION_SIZE], + ) -> PublicKey { + PublicKey { + public_key: CanonicalDeserialize::deserialize_uncompressed_unchecked(&serialized[..]) + .unwrap(), + } + } } impl Eq for PublicKey {} diff --git a/bls/tests/tests.rs b/bls/tests/tests.rs index dd27d648f9..056e5b7a67 100644 --- a/bls/tests/tests.rs +++ b/bls/tests/tests.rs @@ -188,3 +188,17 @@ fn aggregate_signatures_serialization() { &AggregateSignature::deserialize_from_vec(&ser_agg_sig).unwrap() )); } + +#[test] +fn trusted_serialization() { + let hex_public_key = "ae4cc2e31e04add9a6d379b4379b02f302971503cbac8d02fdc5d2dc8204d24ec8d095627d037de747f1a8ea7bf3c1693262d947f78e0cc73c18ecc2f2ec5b2249d551e1680fe0c973a7951bd78d4fbe0326be71286ed34004d2443eb3b00167a02edffcfd2b8539448fa116c5454da2d181dc03ea8cfe3fedb58b9b945d5e506c794deb3ba73983005b3ff799212bf59030a8dd17ff48fd5d015695195a022fed8ba4fab28a4c3e2d6f41be0e6315e41824df161219c02be5a281c215011c13131184187e9100d2d6a5321fd9b154806ecc78e93b91331a5334b8876fd1b8ea62b17ce6045fc9e1af60b7705b0cf86dba79f5bcb8320c99a45f3b7c7178f8f87ba953de2755c61af882059c1de1d7a35357f06cd4a7d954e4bb211900"; + + let raw_public_key: Vec = hex::decode(hex_public_key).unwrap(); + let compressed_public_key = CompressedPublicKey::deserialize_from_vec(&raw_public_key).unwrap(); + + let public_key = compressed_public_key.uncompress().unwrap(); + assert_eq!( + PublicKey::trusted_deserialize(&public_key.trusted_serialize()).public_key, + public_key.public_key, + ); +} diff --git a/web-client/src/client/bls_cache.rs b/web-client/src/client/bls_cache.rs index 5d8e2fec9c..4295610d58 100644 --- a/web-client/src/client/bls_cache.rs +++ b/web-client/src/client/bls_cache.rs @@ -1,6 +1,5 @@ -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress}; use idb::{Database, Error, KeyPath, ObjectStore, TransactionMode}; -use nimiq_bls::{G2Projective, LazyPublicKey, PublicKey}; +use nimiq_bls::{LazyPublicKey, PublicKey}; use nimiq_serde::{Deserialize, Serialize}; /// Caches decompressed BlsPublicKeys in an IndexedDB @@ -12,7 +11,7 @@ pub(crate) struct BlsCache { #[derive(Deserialize, Serialize)] struct BlsKeyEntry { #[serde(with = "serde_bytes")] - public_key: Vec, + public_key: [u8; PublicKey::TRUSTED_SERIALIZATION_SIZE], } const BLS_KEYS: &str = "bls_keys"; @@ -49,15 +48,13 @@ impl BlsCache { let bls_keys_store = transaction.object_store(BLS_KEYS)?; for key in keys { - let mut public_key = Vec::new(); assert!(key.has_uncompressed()); - key.uncompress() - .expect("must not pass invalid keys to `BlsCache::add_keys`") - .public_key - .serialize_with_mode(&mut public_key, Compress::No) - .unwrap(); - - let entry = BlsKeyEntry { public_key }; + let entry = BlsKeyEntry { + public_key: key + .uncompress() + .expect("must not pass invalid keys to `BlsCache::add_keys`") + .trusted_serialize(), + }; let entry_js_value = serde_wasm_bindgen::to_value(&entry).unwrap(); bls_keys_store.put(&entry_js_value, None)?.await?; } @@ -79,9 +76,7 @@ impl BlsCache { for js_key in &js_keys { let value: BlsKeyEntry = serde_wasm_bindgen::from_value(js_key.clone()).unwrap(); - let public_key = PublicKey::new( - G2Projective::deserialize_uncompressed_unchecked(&*value.public_key).unwrap(), - ); + let public_key = PublicKey::trusted_deserialize(&value.public_key); self.keys.push(LazyPublicKey::from(public_key)); } transaction.await?;