Skip to content

Commit

Permalink
crypto: impl ser/de for UserSignature
Browse files Browse the repository at this point in the history
  • Loading branch information
bmwill committed Feb 24, 2024
1 parent 10eb2d5 commit 3c9aa2c
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 51 deletions.
140 changes: 89 additions & 51 deletions src/types/crypto/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,31 +257,51 @@ mod serialization {
}
}

#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[derive(serde_derive::Deserialize)]
pub struct Multisig {
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
committee: MultisigCommittee,
}

#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[derive(serde_derive::Serialize)]
pub struct MultisigRef<'a> {
signatures: &'a [MultisigMemberSignature],
bitmap: BitmapUnit,
committee: &'a MultisigCommittee,
}

#[derive(serde_derive::Deserialize)]
pub struct LegacyMultisig {
signatures: Vec<MultisigMemberSignature>,
#[serde(with = "::serde_with::As::<BinaryRoaringBitmap>")]
bitmap: roaring::RoaringBitmap,
committee: LegacyMultisigCommittee,
}

#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[derive(serde_derive::Serialize)]
pub struct LegacyMultisigRef<'a> {
signatures: &'a [MultisigMemberSignature],
#[serde(with = "::serde_with::As::<BinaryRoaringBitmap>")]
bitmap: &'a roaring::RoaringBitmap,
committee: LegacyMultisigCommitteeRef<'a>,
}

#[derive(serde_derive::Deserialize)]
struct LegacyMultisigCommittee {
/// A list of committee members and their corresponding weight.
#[serde(with = "::serde_with::As::<Vec<(Base64MultisigMember, ::serde_with::Same)>>")]
members: Vec<(MultisigMember, WeightUnit)>,
/// If the total weight of the public keys corresponding to verified signatures is larger than threshold, the Multisig is verified.
threshold: ThresholdUnit,
}

#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
#[derive(serde_derive::Serialize)]
struct LegacyMultisigCommitteeRef<'a> {
#[serde(with = "::serde_with::As::<&[(Base64MultisigMember, ::serde_with::Same)]>")]
members: &'a [(MultisigMember, WeightUnit)],
threshold: ThresholdUnit,
}

#[derive(serde_derive::Deserialize)]
struct ReadableMultisigAggregatedSignature {
signatures: Vec<MultisigMemberSignature>,
bitmap: BitmapUnit,
Expand All @@ -291,39 +311,49 @@ mod serialization {
committee: MultisigCommittee,
}

#[derive(serde_derive::Serialize)]
struct ReadableMultisigAggregatedSignatureRef<'a> {
signatures: &'a [MultisigMemberSignature],
bitmap: BitmapUnit,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(with = "::serde_with::As::<Option<Base64RoaringBitmap>>")]
legacy_bitmap: &'a Option<roaring::RoaringBitmap>,
committee: &'a MultisigCommittee,
}

impl Serialize for MultisigAggregatedSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let readable = ReadableMultisigAggregatedSignature {
signatures: self.signatures.clone(),
let readable = ReadableMultisigAggregatedSignatureRef {
signatures: &self.signatures,
bitmap: self.bitmap,
legacy_bitmap: self.legacy_bitmap.clone(),
committee: self.committee.clone(),
legacy_bitmap: &self.legacy_bitmap,
committee: &self.committee,
};
readable.serialize(serializer)
} else {
let mut buf = Vec::new();
buf.push(SignatureScheme::Multisig as u8);

if let Some(bitmap) = &self.legacy_bitmap {
let legacy = LegacyMultisig {
signatures: self.signatures.clone(),
bitmap: bitmap.clone(),
committee: LegacyMultisigCommittee {
members: self.committee.members.clone(),
let legacy = LegacyMultisigRef {
signatures: &self.signatures,
bitmap,
committee: LegacyMultisigCommitteeRef {
members: &self.committee.members,
threshold: self.committee.threshold,
},
};

bcs::serialize_into(&mut buf, &legacy).expect("serialization cannot fail");
} else {
let multisig = Multisig {
signatures: self.signatures.clone(),
let multisig = MultisigRef {
signatures: &self.signatures,
bitmap: self.bitmap,
committee: self.committee.clone(),
committee: &self.committee,
};
bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail");
}
Expand All @@ -347,39 +377,47 @@ mod serialization {
})
} else {
let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?;
let flag =
SignatureScheme::from_byte(bytes[0]).map_err(serde::de::Error::custom)?;
if flag != SignatureScheme::Multisig {
return Err(serde::de::Error::custom("invalid multisig flag"));
}
let bcs_bytes = &bytes[1..];

// Unfortunately we have no information in the serialized form of a Multisig to be
// able to determine if its a Legacy format or the new standard format so we just
// need to try each.
//
// We'll start with the newer format as that should be more prevalent.
if let Ok(multisig) = bcs::from_bytes::<Multisig>(bcs_bytes) {
Ok(Self {
signatures: multisig.signatures,
bitmap: multisig.bitmap,
legacy_bitmap: None,
committee: multisig.committee,
})
} else if let Ok(legacy) = bcs::from_bytes::<LegacyMultisig>(bcs_bytes) {
Ok(Self {
signatures: legacy.signatures,
bitmap: roaring_bitmap_to_u16(&legacy.bitmap)
.map_err(serde::de::Error::custom)?,
legacy_bitmap: Some(legacy.bitmap),
committee: MultisigCommittee {
members: legacy.committee.members,
threshold: legacy.committee.threshold,
},
})
} else {
Err(serde::de::Error::custom("invalid multisig"))
}
Self::from_serialized_bytes(bytes)
}
}
}

impl MultisigAggregatedSignature {
pub(crate) fn from_serialized_bytes<T: AsRef<[u8]>, E: serde::de::Error>(
bytes: T,
) -> Result<Self, E> {
let bytes = bytes.as_ref();
let flag = SignatureScheme::from_byte(bytes[0]).map_err(serde::de::Error::custom)?;
if flag != SignatureScheme::Multisig {
return Err(serde::de::Error::custom("invalid multisig flag"));
}
let bcs_bytes = &bytes[1..];

// Unfortunately we have no information in the serialized form of a Multisig to be
// able to determine if its a Legacy format or the new standard format so we just
// need to try each.
//
// We'll start with the newer format as that should be more prevalent.
if let Ok(multisig) = bcs::from_bytes::<Multisig>(bcs_bytes) {
Ok(Self {
signatures: multisig.signatures,
bitmap: multisig.bitmap,
legacy_bitmap: None,
committee: multisig.committee,
})
} else if let Ok(legacy) = bcs::from_bytes::<LegacyMultisig>(bcs_bytes) {
Ok(Self {
signatures: legacy.signatures,
bitmap: roaring_bitmap_to_u16(&legacy.bitmap)
.map_err(serde::de::Error::custom)?,
legacy_bitmap: Some(legacy.bitmap),
committee: MultisigCommittee {
members: legacy.committee.members,
threshold: legacy.committee.threshold,
},
})
} else {
Err(serde::de::Error::custom("invalid multisig"))
}
}
}
Expand Down
143 changes: 143 additions & 0 deletions src/types/crypto/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,146 @@ pub enum UserSignature {
Multisig(MultisigAggregatedSignature),
// ZkLoginAuthenticator,
}

#[cfg(feature = "serde")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
mod serialization {
use super::*;

#[derive(serde_derive::Serialize)]
#[serde(tag = "scheme")]
#[serde(rename_all = "lowercase")]
enum ReadableUserSignatureRef<'a> {
Ed25519 {
signature: &'a Ed25519Signature,
public_key: &'a Ed25519PublicKey,
},
Secp256k1 {
signature: &'a Secp256k1Signature,
public_key: &'a Secp256k1PublicKey,
},
Secp256r1 {
signature: &'a Secp256r1Signature,
public_key: &'a Secp256r1PublicKey,
},
Multisig(&'a MultisigAggregatedSignature),
}

#[derive(serde_derive::Deserialize)]
#[serde(tag = "scheme")]
#[serde(rename_all = "lowercase")]
enum ReadableUserSignature {
Ed25519 {
signature: Ed25519Signature,
public_key: Ed25519PublicKey,
},
Secp256k1 {
signature: Secp256k1Signature,
public_key: Secp256k1PublicKey,
},
Secp256r1 {
signature: Secp256r1Signature,
public_key: Secp256r1PublicKey,
},
Multisig(MultisigAggregatedSignature),
}

impl serde::Serialize for UserSignature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let readable = match self {
UserSignature::Simple(SimpleSignature::Ed25519 {
signature,
public_key,
}) => ReadableUserSignatureRef::Ed25519 {
signature,
public_key,
},
UserSignature::Simple(SimpleSignature::Secp256k1 {
signature,
public_key,
}) => ReadableUserSignatureRef::Secp256k1 {
signature,
public_key,
},
UserSignature::Simple(SimpleSignature::Secp256r1 {
signature,
public_key,
}) => ReadableUserSignatureRef::Secp256r1 {
signature,
public_key,
},
UserSignature::Multisig(multisig) => {
ReadableUserSignatureRef::Multisig(multisig)
}
};
readable.serialize(serializer)
} else {
match self {
UserSignature::Simple(simple) => simple.serialize(serializer),
UserSignature::Multisig(multisig) => multisig.serialize(serializer),
}
}
}
}

impl<'de> serde::Deserialize<'de> for UserSignature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let readable = ReadableUserSignature::deserialize(deserializer)?;
Ok(match readable {
ReadableUserSignature::Ed25519 {
signature,
public_key,
} => Self::Simple(SimpleSignature::Ed25519 {
signature,
public_key,
}),
ReadableUserSignature::Secp256k1 {
signature,
public_key,
} => Self::Simple(SimpleSignature::Secp256k1 {
signature,
public_key,
}),
ReadableUserSignature::Secp256r1 {
signature,
public_key,
} => Self::Simple(SimpleSignature::Secp256r1 {
signature,
public_key,
}),
ReadableUserSignature::Multisig(_) => todo!(),
})
} else {
use serde_with::DeserializeAs;

let bytes: std::borrow::Cow<'de, [u8]> =
serde_with::Bytes::deserialize_as(deserializer)?;
let flag =
SignatureScheme::from_byte(bytes[0]).map_err(serde::de::Error::custom)?;
match flag {
SignatureScheme::Ed25519
| SignatureScheme::Secp256k1
| SignatureScheme::Secp256r1 => {
let simple = SimpleSignature::from_serialized_bytes(bytes)?;
Ok(Self::Simple(simple))
}
SignatureScheme::Multisig => {
let multisig = MultisigAggregatedSignature::from_serialized_bytes(bytes)?;
Ok(Self::Multisig(multisig))
}
SignatureScheme::BLS12381 | SignatureScheme::ZkLoginAuthenticator => {
Err(serde::de::Error::custom("invalid signature scheme"))
}
}
}
}
}
}

0 comments on commit 3c9aa2c

Please sign in to comment.