diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 6067c53e0..d5869b6e9 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -332,45 +332,24 @@ impl Debug for G1Element { } } -serialize_deserialize_with_to_from_byte_array!(G1Element); -generate_bytes_representation!(G1Element, G1_ELEMENT_BYTE_LENGTH, G1ElementAsBytes); - -/// An uncompressed serialization of a G1 element. This format is two times longer than the compressed -/// format used by `G1Element::serialize`, but is much faster to deserialize. -/// -/// The intended use of this struct is to deserialize and sum a large number of G1 elements without -/// having to decompress them first. -#[derive(Clone, Debug, PartialEq, Eq)] -#[repr(transparent)] -pub struct G1ElementUncompressed(pub(crate) [u8; 2 * G1_ELEMENT_BYTE_LENGTH]); - -impl From<&G1Element> for G1ElementUncompressed { - fn from(element: &G1Element) -> Self { - let mut bytes = [0u8; 2 * G1_ELEMENT_BYTE_LENGTH]; - unsafe { - blst_p1_serialize(bytes.as_mut_ptr(), &element.0); - } - G1ElementUncompressed(bytes) - } -} - -impl TryFrom<&G1ElementUncompressed> for G1Element { - type Error = FastCryptoError; - - fn try_from(value: &G1ElementUncompressed) -> Result { +impl G1Element { + /// Try to deserialize a G1 element from an uncompressed byte representation.. + pub fn try_from_uncompressed_bytes( + bytes: &[u8; G1_ELEMENT_UNCOMPRESSED_BYTE_LENGTH], + ) -> FastCryptoResult { // See https://github.com/supranational/blst for details on the serialization format. // Note that `blst_p1_deserialize` accepts both compressed and uncompressed serializations, // so we check that the compressed bit flag (the 1st) is not set. The third is used for // compressed points to indicate sign of the y-coordinate and should also not be set. - if value.0[0] & 0x20 != 0 || value.0[0] & 0x80 != 0 { + if bytes[0] & 0x20 != 0 || bytes[0] & 0x80 != 0 { return Err(InvalidInput); } let mut ret = blst_p1::default(); unsafe { let mut affine = blst_p1_affine::default(); - if blst_p1_deserialize(&mut affine, value.0.as_ptr()) != BLST_ERROR::BLST_SUCCESS { + if blst_p1_deserialize(&mut affine, bytes.as_ptr()) != BLST_ERROR::BLST_SUCCESS { return Err(InvalidInput); } blst_p1_from_affine(&mut ret, &affine); @@ -383,26 +362,36 @@ impl TryFrom<&G1ElementUncompressed> for G1Element { } } -impl ToFromByteArray for G1ElementUncompressed { - fn from_byte_array( - bytes: &[u8; G1_ELEMENT_UNCOMPRESSED_BYTE_LENGTH], - ) -> FastCryptoResult { - let uncompressed = Self::from_trusted_byte_array(*bytes); +serialize_deserialize_with_to_from_byte_array!(G1Element); +generate_bytes_representation!(G1Element, G1_ELEMENT_BYTE_LENGTH, G1ElementAsBytes); - // Use the validity check from G1Element::try_from - if G1Element::try_from(&uncompressed).is_err() { - return Err(InvalidInput); +/// An uncompressed serialization of a G1 element. This format is two times longer than the compressed +/// format used by `G1Element::serialize`, but is much faster to deserialize. +/// +/// The intended use of this struct is to deserialize and sum a large number of G1 elements without +/// having to decompress them first. +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct G1ElementUncompressed(pub(crate) [u8; 2 * G1_ELEMENT_BYTE_LENGTH]); + +impl From<&G1Element> for G1ElementUncompressed { + fn from(element: &G1Element) -> Self { + let mut bytes = [0u8; 2 * G1_ELEMENT_BYTE_LENGTH]; + unsafe { + blst_p1_serialize(bytes.as_mut_ptr(), &element.0); } - Ok(uncompressed) + G1ElementUncompressed(bytes) } +} - fn to_byte_array(&self) -> [u8; G1_ELEMENT_UNCOMPRESSED_BYTE_LENGTH] { - self.0 +impl TryFrom<&G1ElementUncompressed> for G1Element { + type Error = FastCryptoError; + + fn try_from(value: &G1ElementUncompressed) -> Result { + G1Element::try_from_uncompressed_bytes(&value.0) } } -serialize_deserialize_with_to_from_byte_array!(G1ElementUncompressed); - impl G1ElementUncompressed { /// Create a new `G1ElementUncompressed` from a byte array. /// The input is not validated so it should come from a trusted source. diff --git a/fastcrypto/src/tests/bls12381_group_tests.rs b/fastcrypto/src/tests/bls12381_group_tests.rs index 718f58688..09275a8db 100644 --- a/fastcrypto/src/tests/bls12381_group_tests.rs +++ b/fastcrypto/src/tests/bls12381_group_tests.rs @@ -393,12 +393,10 @@ fn test_serde_and_regression() { let g1 = G1Element::generator(); let g2 = G2Element::generator(); let gt = GTElement::generator(); - let ug1 = G1ElementUncompressed::from(&g1); let id1 = G1Element::zero(); let id2 = G2Element::zero(); let id3 = GTElement::zero(); let id4 = Scalar::zero(); - let id5 = G1ElementUncompressed::from(&id1); verify_serialization( &s1, @@ -411,7 +409,6 @@ fn test_serde_and_regression() { verify_serialization(&g1, Some(hex::decode("97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb").unwrap().as_slice())); verify_serialization(&g2, Some(hex::decode("93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8").unwrap().as_slice())); verify_serialization(>, Some(hex::decode("1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba5703350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a201b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b604c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631").unwrap().as_slice())); - verify_serialization(&ug1, Some(hex::decode("17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1").unwrap().as_slice())); verify_serialization(&id1, Some(hex::decode("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().as_slice())); verify_serialization(&id2, Some(hex::decode("c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().as_slice())); verify_serialization(&id3, Some(hex::decode("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().as_slice())); @@ -423,7 +420,6 @@ fn test_serde_and_regression() { .as_slice(), ), ); - verify_serialization(&id5, Some(hex::decode("400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().as_slice())); } #[test] @@ -545,57 +541,6 @@ fn test_serialization_g1() { assert!(G1Element::from_byte_array(&bytes).is_err()); } -#[test] -fn test_serialization_uncompressed_g1() { - let infinity_bit = 0x40; - let compressed_bit = 0x80; - - // All zero serialization for G1 should fail. - let mut bytes = [0u8; 96]; - assert!(G1ElementUncompressed::from_byte_array(&bytes).is_err()); - // fromTrustedByteArray accepts invalid points. - let uncompressed = G1ElementUncompressed::from_trusted_byte_array(bytes); - // But trying to convert to G1Element fails. - assert!(G1Element::try_from(&uncompressed).is_err()); - - // Valid infinity - bytes[0] = infinity_bit; - assert_eq!( - G1ElementUncompressed::from(&G1Element::zero()), - G1ElementUncompressed::from_byte_array(&bytes).unwrap() - ); - - // Setting the compressed bit should fail - bytes[0] |= compressed_bit; - assert!(G1ElementUncompressed::from_byte_array(&bytes).is_err()); - - // But if we take the first 48 bytes as a compressed serialization, it should work. - let compressed_bytes = bytes[0..48].try_into().unwrap(); - assert_eq!( - G1Element::zero(), - G1Element::from_byte_array(&compressed_bytes).unwrap() - ); - - // to and from_byte_array should be inverses. - let mut bytes = G1ElementUncompressed::from(&G1Element::generator()).to_byte_array(); - assert_eq!( - G1ElementUncompressed::from(&G1Element::generator()), - G1ElementUncompressed::from_byte_array(&bytes).unwrap() - ); - assert_eq!(bytes[0] & compressed_bit, 0); - - // Setting the compressed bit set, this should fail. - bytes[0] |= compressed_bit; - assert!(G1ElementUncompressed::from_byte_array(&bytes).is_err()); - - // But if we take the first 48 bytes as a compressed serialization, it should work. - let compressed_bytes = bytes[0..48].try_into().unwrap(); - assert_eq!( - G1Element::generator(), - G1Element::from_byte_array(&compressed_bytes).unwrap() - ); -} - #[test] fn test_serialization_g2() { let infinity_bit = 0x40;