use std::{
collections::BTreeMap,
fmt::{self, Debug, Display, Formatter},
hash::{Hash, Hasher},
str::FromStr,
};
use anyhow::{Error, anyhow};
use derive_more::{AsMut, AsRef, From};
pub use enum_dispatch::enum_dispatch;
use eyre::eyre;
pub use fastcrypto::traits::{
AggregateAuthenticator, Authenticator, EncodeDecodeBase64, KeyPair as KeypairTraits, Signer,
SigningKey, ToFromBytes, VerifyingKey,
};
use fastcrypto::{
bls12381::min_sig::{
BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair,
BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature,
},
ed25519::{
Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes,
Ed25519Signature, Ed25519SignatureAsBytes,
},
encoding::{Base64, Bech32, Encoding, Hex},
error::{FastCryptoError, FastCryptoResult},
hash::{Blake2b256, HashFunction},
secp256k1::{
Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature,
Secp256k1SignatureAsBytes,
},
secp256r1::{
Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature,
Secp256r1SignatureAsBytes,
},
};
use fastcrypto_zkp::{bn254::zk_login::ZkLoginInputs, zk_login_utils::Bn254FrElement};
use rand::{
SeedableRng,
rngs::{OsRng, StdRng},
};
use roaring::RoaringBitmap;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, ser::Serializer};
use serde_with::{Bytes, serde_as};
use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
use strum::EnumString;
use tracing::{instrument, warn};
use crate::{
base_types::{AuthorityName, ConciseableName, IotaAddress},
committee::{Committee, CommitteeTrait, EpochId, StakeUnit},
error::{IotaError, IotaResult},
iota_serde::{IotaBitmap, Readable},
signature::GenericSignature,
};
#[cfg(test)]
#[path = "unit_tests/crypto_tests.rs"]
mod crypto_tests;
#[cfg(test)]
#[cfg(feature = "test-utils")]
#[path = "unit_tests/intent_tests.rs"]
mod intent_tests;
pub type AuthorityKeyPair = BLS12381KeyPair;
pub type AuthorityPublicKey = BLS12381PublicKey;
pub type AuthorityPrivateKey = BLS12381PrivateKey;
pub type AuthoritySignature = BLS12381Signature;
pub type AggregateAuthoritySignature = BLS12381AggregateSignature;
pub type AggregateAuthoritySignatureAsBytes = BLS12381AggregateSignatureAsBytes;
pub type AccountKeyPair = Ed25519KeyPair;
pub type AccountPublicKey = Ed25519PublicKey;
pub type AccountPrivateKey = Ed25519PrivateKey;
pub type NetworkKeyPair = Ed25519KeyPair;
pub type NetworkPublicKey = Ed25519PublicKey;
pub type NetworkPrivateKey = Ed25519PrivateKey;
pub type DefaultHash = Blake2b256;
pub const DEFAULT_EPOCH_ID: EpochId = 0;
pub const IOTA_PRIV_KEY_PREFIX: &str = "iotaprivkey";
pub fn generate_proof_of_possession(
keypair: &AuthorityKeyPair,
address: IotaAddress,
) -> AuthoritySignature {
let mut msg: Vec<u8> = Vec::new();
msg.extend_from_slice(keypair.public().as_bytes());
msg.extend_from_slice(address.as_ref());
AuthoritySignature::new_secure(
&IntentMessage::new(Intent::iota_app(IntentScope::ProofOfPossession), msg),
&DEFAULT_EPOCH_ID,
keypair,
)
}
pub fn verify_proof_of_possession(
pop: &AuthoritySignature,
authority_pubkey: &AuthorityPublicKey,
iota_address: IotaAddress,
) -> Result<(), IotaError> {
authority_pubkey
.validate()
.map_err(|_| IotaError::InvalidSignature {
error: "Fail to validate pubkey".to_string(),
})?;
let mut msg = authority_pubkey.as_bytes().to_vec();
msg.extend_from_slice(iota_address.as_ref());
pop.verify_secure(
&IntentMessage::new(Intent::iota_app(IntentScope::ProofOfPossession), msg),
DEFAULT_EPOCH_ID,
authority_pubkey.into(),
)
}
#[expect(clippy::large_enum_variant)]
#[derive(Debug, From, PartialEq, Eq)]
pub enum IotaKeyPair {
Ed25519(Ed25519KeyPair),
Secp256k1(Secp256k1KeyPair),
Secp256r1(Secp256r1KeyPair),
}
impl IotaKeyPair {
pub fn public(&self) -> PublicKey {
match self {
IotaKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()),
IotaKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()),
IotaKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()),
}
}
pub fn copy(&self) -> Self {
match self {
IotaKeyPair::Ed25519(kp) => kp.copy().into(),
IotaKeyPair::Secp256k1(kp) => kp.copy().into(),
IotaKeyPair::Secp256r1(kp) => kp.copy().into(),
}
}
}
impl Signer<Signature> for IotaKeyPair {
fn sign(&self, msg: &[u8]) -> Signature {
match self {
IotaKeyPair::Ed25519(kp) => kp.sign(msg),
IotaKeyPair::Secp256k1(kp) => kp.sign(msg),
IotaKeyPair::Secp256r1(kp) => kp.sign(msg),
}
}
}
impl EncodeDecodeBase64 for IotaKeyPair {
fn encode_base64(&self) -> String {
Base64::encode(self.to_bytes())
}
fn decode_base64(value: &str) -> FastCryptoResult<Self> {
let bytes = Base64::decode(value)?;
Self::from_bytes(&bytes).map_err(|_| FastCryptoError::InvalidInput)
}
}
impl IotaKeyPair {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
bytes.push(self.public().flag());
match self {
IotaKeyPair::Ed25519(kp) => {
bytes.extend_from_slice(kp.as_bytes());
}
IotaKeyPair::Secp256k1(kp) => {
bytes.extend_from_slice(kp.as_bytes());
}
IotaKeyPair::Secp256r1(kp) => {
bytes.extend_from_slice(kp.as_bytes());
}
}
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, eyre::Report> {
match SignatureScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?)
{
Ok(x) => match x {
SignatureScheme::ED25519 => Ok(IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?)),
SignatureScheme::Secp256k1 => {
Ok(IotaKeyPair::Secp256k1(Secp256k1KeyPair::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?))
}
SignatureScheme::Secp256r1 => {
Ok(IotaKeyPair::Secp256r1(Secp256r1KeyPair::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?))
}
_ => Err(eyre!("Invalid flag byte")),
},
_ => Err(eyre!("Invalid bytes")),
}
}
pub fn to_bytes_no_flag(&self) -> Vec<u8> {
match self {
IotaKeyPair::Ed25519(kp) => kp.as_bytes().to_vec(),
IotaKeyPair::Secp256k1(kp) => kp.as_bytes().to_vec(),
IotaKeyPair::Secp256r1(kp) => kp.as_bytes().to_vec(),
}
}
pub fn encode(&self) -> Result<String, eyre::Report> {
Bech32::encode(self.to_bytes(), IOTA_PRIV_KEY_PREFIX).map_err(|e| eyre!(e))
}
pub fn decode(value: &str) -> Result<Self, eyre::Report> {
let bytes = Bech32::decode(value, IOTA_PRIV_KEY_PREFIX)?;
Self::from_bytes(&bytes)
}
}
impl Serialize for IotaKeyPair {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = self.encode_base64();
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for IotaKeyPair {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let s = String::deserialize(deserializer)?;
IotaKeyPair::decode_base64(&s).map_err(|e| Error::custom(e.to_string()))
}
}
#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
pub enum PublicKey {
Ed25519(Ed25519PublicKeyAsBytes),
Secp256k1(Secp256k1PublicKeyAsBytes),
Secp256r1(Secp256r1PublicKeyAsBytes),
ZkLogin(ZkLoginPublicIdentifier),
Passkey(Secp256r1PublicKeyAsBytes),
}
#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
pub struct ZkLoginPublicIdentifier(#[schemars(with = "Base64")] pub Vec<u8>);
impl ZkLoginPublicIdentifier {
pub fn new(iss: &str, address_seed: &Bn254FrElement) -> IotaResult<Self> {
let mut bytes = Vec::new();
let iss_bytes = iss.as_bytes();
bytes.extend([iss_bytes.len() as u8]);
bytes.extend(iss_bytes);
bytes.extend(address_seed.padded());
Ok(Self(bytes))
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
match self {
PublicKey::Ed25519(pk) => &pk.0,
PublicKey::Secp256k1(pk) => &pk.0,
PublicKey::Secp256r1(pk) => &pk.0,
PublicKey::ZkLogin(z) => &z.0,
PublicKey::Passkey(pk) => &pk.0,
}
}
}
impl EncodeDecodeBase64 for PublicKey {
fn encode_base64(&self) -> String {
let mut bytes: Vec<u8> = Vec::new();
bytes.extend_from_slice(&[self.flag()]);
bytes.extend_from_slice(self.as_ref());
Base64::encode(&bytes[..])
}
fn decode_base64(value: &str) -> FastCryptoResult<Self> {
let bytes = Base64::decode(value)?;
match bytes.first() {
Some(x) => {
if x == &SignatureScheme::ED25519.flag() {
let pk: Ed25519PublicKey =
Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or(
FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1),
)?)?;
Ok(PublicKey::Ed25519((&pk).into()))
} else if x == &SignatureScheme::Secp256k1.flag() {
let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or(
FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1),
)?)?;
Ok(PublicKey::Secp256k1((&pk).into()))
} else if x == &SignatureScheme::Secp256r1.flag() {
let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or(
FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1),
)?)?;
Ok(PublicKey::Secp256r1((&pk).into()))
} else if x == &SignatureScheme::PasskeyAuthenticator.flag() {
let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or(
FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1),
)?)?;
Ok(PublicKey::Passkey((&pk).into()))
} else {
Err(FastCryptoError::InvalidInput)
}
}
_ => Err(FastCryptoError::InvalidInput),
}
}
}
impl PublicKey {
pub fn flag(&self) -> u8 {
self.scheme().flag()
}
pub fn try_from_bytes(
curve: SignatureScheme,
key_bytes: &[u8],
) -> Result<PublicKey, eyre::Report> {
match curve {
SignatureScheme::ED25519 => Ok(PublicKey::Ed25519(
(&Ed25519PublicKey::from_bytes(key_bytes)?).into(),
)),
SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(
(&Secp256k1PublicKey::from_bytes(key_bytes)?).into(),
)),
SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(
(&Secp256r1PublicKey::from_bytes(key_bytes)?).into(),
)),
SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey(
(&Secp256r1PublicKey::from_bytes(key_bytes)?).into(),
)),
_ => Err(eyre!("Unsupported curve")),
}
}
pub fn scheme(&self) -> SignatureScheme {
match self {
PublicKey::Ed25519(_) => Ed25519IotaSignature::SCHEME,
PublicKey::Secp256k1(_) => Secp256k1IotaSignature::SCHEME,
PublicKey::Secp256r1(_) => Secp256r1IotaSignature::SCHEME,
PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator,
PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator,
}
}
pub fn from_zklogin_inputs(inputs: &ZkLoginInputs) -> IotaResult<Self> {
Ok(PublicKey::ZkLogin(ZkLoginPublicIdentifier::new(
inputs.get_iss(),
inputs.get_address_seed(),
)?))
}
}
#[serde_as]
#[derive(
Copy,
Clone,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
schemars::JsonSchema,
AsRef,
)]
#[as_ref(forward)]
pub struct AuthorityPublicKeyBytes(
#[schemars(with = "Base64")]
#[serde_as(as = "Readable<Base64, Bytes>")]
pub [u8; AuthorityPublicKey::LENGTH],
);
impl AuthorityPublicKeyBytes {
fn fmt_impl(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let s = Hex::encode(self.0);
write!(f, "k#{}", s)?;
Ok(())
}
}
impl<'a> ConciseableName<'a> for AuthorityPublicKeyBytes {
type ConciseTypeRef = ConciseAuthorityPublicKeyBytesRef<'a>;
type ConciseType = ConciseAuthorityPublicKeyBytes;
fn concise(&'a self) -> ConciseAuthorityPublicKeyBytesRef<'a> {
ConciseAuthorityPublicKeyBytesRef(self)
}
fn concise_owned(&self) -> ConciseAuthorityPublicKeyBytes {
ConciseAuthorityPublicKeyBytes(*self)
}
}
pub struct ConciseAuthorityPublicKeyBytesRef<'a>(&'a AuthorityPublicKeyBytes);
impl Debug for ConciseAuthorityPublicKeyBytesRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let s = Hex::encode(self.0.0.get(0..4).ok_or(std::fmt::Error)?);
write!(f, "k#{}..", s)
}
}
impl Display for ConciseAuthorityPublicKeyBytesRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
Debug::fmt(self, f)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ConciseAuthorityPublicKeyBytes(AuthorityPublicKeyBytes);
impl Debug for ConciseAuthorityPublicKeyBytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
let s = Hex::encode(self.0.0.get(0..4).ok_or(std::fmt::Error)?);
write!(f, "k#{}..", s)
}
}
impl Display for ConciseAuthorityPublicKeyBytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
Debug::fmt(self, f)
}
}
impl TryFrom<AuthorityPublicKeyBytes> for AuthorityPublicKey {
type Error = FastCryptoError;
fn try_from(bytes: AuthorityPublicKeyBytes) -> Result<AuthorityPublicKey, Self::Error> {
AuthorityPublicKey::from_bytes(bytes.as_ref())
}
}
impl From<&AuthorityPublicKey> for AuthorityPublicKeyBytes {
fn from(pk: &AuthorityPublicKey) -> AuthorityPublicKeyBytes {
AuthorityPublicKeyBytes::from_bytes(pk.as_ref()).unwrap()
}
}
impl Debug for AuthorityPublicKeyBytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.fmt_impl(f)
}
}
impl Display for AuthorityPublicKeyBytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
self.fmt_impl(f)
}
}
impl ToFromBytes for AuthorityPublicKeyBytes {
fn from_bytes(bytes: &[u8]) -> Result<Self, fastcrypto::error::FastCryptoError> {
let bytes: [u8; AuthorityPublicKey::LENGTH] = bytes
.try_into()
.map_err(|_| fastcrypto::error::FastCryptoError::InvalidInput)?;
Ok(AuthorityPublicKeyBytes(bytes))
}
}
impl AuthorityPublicKeyBytes {
pub const ZERO: Self = Self::new([0u8; AuthorityPublicKey::LENGTH]);
pub const fn new(bytes: [u8; AuthorityPublicKey::LENGTH]) -> AuthorityPublicKeyBytes
where {
AuthorityPublicKeyBytes(bytes)
}
}
impl FromStr for AuthorityPublicKeyBytes {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = Hex::decode(s).map_err(|e| anyhow!(e))?;
Self::from_bytes(&value[..]).map_err(|e| anyhow!(e))
}
}
impl Default for AuthorityPublicKeyBytes {
fn default() -> Self {
Self::ZERO
}
}
pub trait IotaAuthoritySignature {
fn verify_secure<T>(
&self,
value: &IntentMessage<T>,
epoch_id: EpochId,
author: AuthorityPublicKeyBytes,
) -> Result<(), IotaError>
where
T: Serialize;
fn new_secure<T>(
value: &IntentMessage<T>,
epoch_id: &EpochId,
secret: &dyn Signer<Self>,
) -> Self
where
T: Serialize;
}
impl IotaAuthoritySignature for AuthoritySignature {
#[instrument(level = "trace", skip_all)]
fn new_secure<T>(value: &IntentMessage<T>, epoch: &EpochId, secret: &dyn Signer<Self>) -> Self
where
T: Serialize,
{
let mut intent_msg_bytes =
bcs::to_bytes(&value).expect("Message serialization should not fail");
epoch.write(&mut intent_msg_bytes);
secret.sign(&intent_msg_bytes)
}
#[instrument(level = "trace", skip_all)]
fn verify_secure<T>(
&self,
value: &IntentMessage<T>,
epoch: EpochId,
author: AuthorityPublicKeyBytes,
) -> Result<(), IotaError>
where
T: Serialize,
{
let mut message = bcs::to_bytes(&value).expect("Message serialization should not fail");
epoch.write(&mut message);
let public_key = AuthorityPublicKey::try_from(author).map_err(|_| {
IotaError::KeyConversion(
"Failed to serialize public key bytes to valid public key".to_string(),
)
})?;
public_key
.verify(&message[..], self)
.map_err(|e| IotaError::InvalidSignature {
error: format!(
"Fail to verify auth sig {} epoch: {} author: {}",
e,
epoch,
author.concise()
),
})
}
}
pub fn get_key_pair<KP: KeypairTraits>() -> (IotaAddress, KP)
where
<KP as KeypairTraits>::PubKey: IotaPublicKey,
{
get_key_pair_from_rng(&mut OsRng)
}
pub fn random_committee_key_pairs_of_size(size: usize) -> Vec<AuthorityKeyPair> {
let mut rng = StdRng::from_seed([0; 32]);
(0..size)
.map(|_| {
let key_pair = get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut rng);
get_key_pair_from_rng::<AuthorityKeyPair, _>(&mut rng);
get_key_pair_from_rng::<AccountKeyPair, _>(&mut rng);
get_key_pair_from_rng::<AccountKeyPair, _>(&mut rng);
key_pair.1
})
.collect()
}
pub fn deterministic_random_account_key() -> (IotaAddress, AccountKeyPair) {
let mut rng = StdRng::from_seed([0; 32]);
get_key_pair_from_rng(&mut rng)
}
pub fn get_account_key_pair() -> (IotaAddress, AccountKeyPair) {
get_key_pair()
}
pub fn get_authority_key_pair() -> (IotaAddress, AuthorityKeyPair) {
get_key_pair()
}
pub fn get_key_pair_from_rng<KP: KeypairTraits, R>(csprng: &mut R) -> (IotaAddress, KP)
where
R: rand::CryptoRng + rand::RngCore,
<KP as KeypairTraits>::PubKey: IotaPublicKey,
{
let kp = KP::generate(&mut StdRng::from_rng(csprng).unwrap());
(kp.public().into(), kp)
}
pub fn get_key_pair_from_bytes<KP: KeypairTraits>(bytes: &[u8]) -> IotaResult<(IotaAddress, KP)>
where
<KP as KeypairTraits>::PubKey: IotaPublicKey,
{
let priv_length = <KP as KeypairTraits>::PrivKey::LENGTH;
let pub_key_length = <KP as KeypairTraits>::PubKey::LENGTH;
if bytes.len() != priv_length + pub_key_length {
return Err(IotaError::KeyConversion(format!(
"Invalid input byte length, expected {}: {}",
priv_length,
bytes.len()
)));
}
let sk = <KP as KeypairTraits>::PrivKey::from_bytes(
bytes
.get(..priv_length)
.ok_or(IotaError::InvalidPrivateKey)?,
)
.map_err(|_| IotaError::InvalidPrivateKey)?;
let kp: KP = sk.into();
Ok((kp.public().into(), kp))
}
#[enum_dispatch]
#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)]
pub enum Signature {
Ed25519IotaSignature,
Secp256k1IotaSignature,
Secp256r1IotaSignature,
}
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let bytes = self.as_ref();
if serializer.is_human_readable() {
let s = Base64::encode(bytes);
serializer.serialize_str(&s)
} else {
serializer.serialize_bytes(bytes)
}
}
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let bytes = if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))?
} else {
let data: Vec<u8> = Vec::deserialize(deserializer)?;
data
};
Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string()))
}
}
impl Signature {
pub fn new_hashed(hashed_msg: &[u8], secret: &dyn Signer<Signature>) -> Self {
Signer::sign(secret, hashed_msg)
}
pub fn new_secure<T>(value: &IntentMessage<T>, secret: &dyn Signer<Signature>) -> Self
where
T: Serialize,
{
let mut hasher = DefaultHash::default();
hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail"));
Signer::sign(secret, &hasher.finalize().digest)
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
match self {
Signature::Ed25519IotaSignature(sig) => sig.as_ref(),
Signature::Secp256k1IotaSignature(sig) => sig.as_ref(),
Signature::Secp256r1IotaSignature(sig) => sig.as_ref(),
}
}
}
impl AsMut<[u8]> for Signature {
fn as_mut(&mut self) -> &mut [u8] {
match self {
Signature::Ed25519IotaSignature(sig) => sig.as_mut(),
Signature::Secp256k1IotaSignature(sig) => sig.as_mut(),
Signature::Secp256r1IotaSignature(sig) => sig.as_mut(),
}
}
}
impl ToFromBytes for Signature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
match bytes.first() {
Some(x) => {
if x == &Ed25519IotaSignature::SCHEME.flag() {
Ok(<Ed25519IotaSignature as ToFromBytes>::from_bytes(bytes)?.into())
} else if x == &Secp256k1IotaSignature::SCHEME.flag() {
Ok(<Secp256k1IotaSignature as ToFromBytes>::from_bytes(bytes)?.into())
} else if x == &Secp256r1IotaSignature::SCHEME.flag() {
Ok(<Secp256r1IotaSignature as ToFromBytes>::from_bytes(bytes)?.into())
} else {
Err(FastCryptoError::InvalidInput)
}
}
_ => Err(FastCryptoError::InvalidInput),
}
}
}
impl IotaPublicKey for BLS12381PublicKey {
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::BLS12381;
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
#[as_ref(forward)]
#[as_mut(forward)]
pub struct Ed25519IotaSignature(
#[schemars(with = "Base64")]
#[serde_as(as = "Readable<Base64, Bytes>")]
[u8; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1],
);
impl Default for Ed25519IotaSignature {
fn default() -> Self {
Self([0; Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1])
}
}
impl IotaSignatureInner for Ed25519IotaSignature {
type Sig = Ed25519Signature;
type PubKey = Ed25519PublicKey;
type KeyPair = Ed25519KeyPair;
const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1;
}
impl IotaPublicKey for Ed25519PublicKey {
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519;
}
impl ToFromBytes for Ed25519IotaSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
if bytes.len() != Self::LENGTH {
return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
}
let mut sig_bytes = [0; Self::LENGTH];
sig_bytes.copy_from_slice(bytes);
Ok(Self(sig_bytes))
}
}
impl Signer<Signature> for Ed25519KeyPair {
fn sign(&self, msg: &[u8]) -> Signature {
Ed25519IotaSignature::new(self, msg).into()
}
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
#[as_ref(forward)]
#[as_mut(forward)]
pub struct Secp256k1IotaSignature(
#[schemars(with = "Base64")]
#[serde_as(as = "Readable<Base64, Bytes>")]
[u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1],
);
impl IotaSignatureInner for Secp256k1IotaSignature {
type Sig = Secp256k1Signature;
type PubKey = Secp256k1PublicKey;
type KeyPair = Secp256k1KeyPair;
const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1;
}
impl IotaPublicKey for Secp256k1PublicKey {
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1;
}
impl ToFromBytes for Secp256k1IotaSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
if bytes.len() != Self::LENGTH {
return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
}
let mut sig_bytes = [0; Self::LENGTH];
sig_bytes.copy_from_slice(bytes);
Ok(Self(sig_bytes))
}
}
impl Signer<Signature> for Secp256k1KeyPair {
fn sign(&self, msg: &[u8]) -> Signature {
Secp256k1IotaSignature::new(self, msg).into()
}
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)]
#[as_ref(forward)]
#[as_mut(forward)]
pub struct Secp256r1IotaSignature(
#[schemars(with = "Base64")]
#[serde_as(as = "Readable<Base64, Bytes>")]
[u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1],
);
impl IotaSignatureInner for Secp256r1IotaSignature {
type Sig = Secp256r1Signature;
type PubKey = Secp256r1PublicKey;
type KeyPair = Secp256r1KeyPair;
const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1;
}
impl IotaPublicKey for Secp256r1PublicKey {
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1;
}
impl ToFromBytes for Secp256r1IotaSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, FastCryptoError> {
if bytes.len() != Self::LENGTH {
return Err(FastCryptoError::InputLengthWrong(Self::LENGTH));
}
let mut sig_bytes = [0; Self::LENGTH];
sig_bytes.copy_from_slice(bytes);
Ok(Self(sig_bytes))
}
}
impl Signer<Signature> for Secp256r1KeyPair {
fn sign(&self, msg: &[u8]) -> Signature {
Secp256r1IotaSignature::new(self, msg).into()
}
}
pub trait IotaSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash {
type Sig: Authenticator<PubKey = Self::PubKey>;
type PubKey: VerifyingKey<Sig = Self::Sig> + IotaPublicKey;
type KeyPair: KeypairTraits<PubKey = Self::PubKey, Sig = Self::Sig>;
const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1;
const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME;
fn get_verification_inputs(&self) -> IotaResult<(Self::Sig, Self::PubKey)> {
let pk = Self::PubKey::from_bytes(self.public_key_bytes())
.map_err(|_| IotaError::KeyConversion("Invalid public key".to_string()))?;
let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| {
IotaError::InvalidSignature {
error: "Fail to get pubkey and sig".to_string(),
}
})?;
Ok((signature, pk))
}
fn new(kp: &Self::KeyPair, message: &[u8]) -> Self {
let sig = Signer::sign(kp, message);
let mut signature_bytes: Vec<u8> = Vec::new();
signature_bytes
.extend_from_slice(&[<Self::PubKey as IotaPublicKey>::SIGNATURE_SCHEME.flag()]);
signature_bytes.extend_from_slice(sig.as_ref());
signature_bytes.extend_from_slice(kp.public().as_ref());
Self::from_bytes(&signature_bytes[..])
.expect("Serialized signature did not have expected size")
}
}
pub trait IotaPublicKey: VerifyingKey {
const SIGNATURE_SCHEME: SignatureScheme;
}
#[enum_dispatch(Signature)]
pub trait IotaSignature: Sized + ToFromBytes {
fn signature_bytes(&self) -> &[u8];
fn public_key_bytes(&self) -> &[u8];
fn scheme(&self) -> SignatureScheme;
fn verify_secure<T>(
&self,
value: &IntentMessage<T>,
author: IotaAddress,
scheme: SignatureScheme,
) -> IotaResult<()>
where
T: Serialize;
}
impl<S: IotaSignatureInner + Sized> IotaSignature for S {
fn signature_bytes(&self) -> &[u8] {
&self.as_ref()[1..1 + S::Sig::LENGTH]
}
fn public_key_bytes(&self) -> &[u8] {
&self.as_ref()[S::Sig::LENGTH + 1..]
}
fn scheme(&self) -> SignatureScheme {
S::PubKey::SIGNATURE_SCHEME
}
fn verify_secure<T>(
&self,
value: &IntentMessage<T>,
author: IotaAddress,
scheme: SignatureScheme,
) -> Result<(), IotaError>
where
T: Serialize,
{
let mut hasher = DefaultHash::default();
hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail"));
let digest = hasher.finalize().digest;
let (sig, pk) = &self.get_verification_inputs()?;
match scheme {
SignatureScheme::ZkLoginAuthenticator => {} _ => {
let address = IotaAddress::from(pk);
if author != address {
return Err(IotaError::IncorrectSigner {
error: format!(
"Incorrect signer, expected {:?}, got {:?}",
author, address
),
});
}
}
}
pk.verify(&digest, sig)
.map_err(|e| IotaError::InvalidSignature {
error: format!("Fail to verify user sig {}", e),
})
}
}
pub trait AuthoritySignInfoTrait: private::SealedAuthoritySignInfoTrait {
fn verify_secure<T: Serialize>(
&self,
data: &T,
intent: Intent,
committee: &Committee,
) -> IotaResult;
fn add_to_verification_obligation<'a>(
&self,
committee: &'a Committee,
obligation: &mut VerificationObligation<'a>,
message_index: usize,
) -> IotaResult<()>;
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct EmptySignInfo {}
impl AuthoritySignInfoTrait for EmptySignInfo {
fn verify_secure<T: Serialize>(
&self,
_data: &T,
_intent: Intent,
_committee: &Committee,
) -> IotaResult {
Ok(())
}
fn add_to_verification_obligation<'a>(
&self,
_committee: &'a Committee,
_obligation: &mut VerificationObligation<'a>,
_message_index: usize,
) -> IotaResult<()> {
Ok(())
}
}
#[derive(Clone, Debug, Eq, Serialize, Deserialize)]
pub struct AuthoritySignInfo {
pub epoch: EpochId,
pub authority: AuthorityName,
pub signature: AuthoritySignature,
}
impl AuthoritySignInfoTrait for AuthoritySignInfo {
fn verify_secure<T: Serialize>(
&self,
data: &T,
intent: Intent,
committee: &Committee,
) -> IotaResult<()> {
let mut obligation = VerificationObligation::default();
let idx = obligation.add_message(data, self.epoch, intent);
self.add_to_verification_obligation(committee, &mut obligation, idx)?;
obligation.verify_all()?;
Ok(())
}
fn add_to_verification_obligation<'a>(
&self,
committee: &'a Committee,
obligation: &mut VerificationObligation<'a>,
message_index: usize,
) -> IotaResult<()> {
fp_ensure!(self.epoch == committee.epoch(), IotaError::WrongEpoch {
expected_epoch: committee.epoch(),
actual_epoch: self.epoch,
});
let weight = committee.weight(&self.authority);
fp_ensure!(weight > 0, IotaError::UnknownSigner {
signer: Some(self.authority.concise().to_string()),
index: None,
committee: Box::new(committee.clone())
});
obligation
.public_keys
.get_mut(message_index)
.ok_or(IotaError::InvalidAddress)?
.push(committee.public_key(&self.authority)?);
obligation
.signatures
.get_mut(message_index)
.ok_or(IotaError::InvalidAddress)?
.add_signature(self.signature.clone())
.map_err(|_| IotaError::InvalidSignature {
error: "Fail to aggregator auth sig".to_string(),
})?;
Ok(())
}
}
impl AuthoritySignInfo {
pub fn new<T>(
epoch: EpochId,
value: &T,
intent: Intent,
name: AuthorityName,
secret: &dyn Signer<AuthoritySignature>,
) -> Self
where
T: Serialize,
{
Self {
epoch,
authority: name,
signature: AuthoritySignature::new_secure(
&IntentMessage::new(intent, value),
&epoch,
secret,
),
}
}
}
impl Hash for AuthoritySignInfo {
fn hash<H: Hasher>(&self, state: &mut H) {
self.epoch.hash(state);
self.authority.hash(state);
}
}
impl Display for AuthoritySignInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AuthoritySignInfo {{ epoch: {:?}, authority: {} }}",
self.epoch, self.authority,
)
}
}
impl PartialEq for AuthoritySignInfo {
fn eq(&self, other: &Self) -> bool {
self.epoch == other.epoch && self.authority == other.authority
}
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct AuthorityQuorumSignInfo<const STRONG_THRESHOLD: bool> {
pub epoch: EpochId,
#[schemars(with = "Base64")]
pub signature: AggregateAuthoritySignature,
#[schemars(with = "Base64")]
#[serde_as(as = "IotaBitmap")]
pub signers_map: RoaringBitmap,
}
pub type AuthorityStrongQuorumSignInfo = AuthorityQuorumSignInfo<true>;
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct IotaAuthorityStrongQuorumSignInfo {
pub epoch: EpochId,
pub signature: AggregateAuthoritySignatureAsBytes,
#[schemars(with = "Base64")]
#[serde_as(as = "IotaBitmap")]
pub signers_map: RoaringBitmap,
}
impl From<&AuthorityStrongQuorumSignInfo> for IotaAuthorityStrongQuorumSignInfo {
fn from(info: &AuthorityStrongQuorumSignInfo) -> Self {
Self {
epoch: info.epoch,
signature: (&info.signature).into(),
signers_map: info.signers_map.clone(),
}
}
}
impl TryFrom<&IotaAuthorityStrongQuorumSignInfo> for AuthorityStrongQuorumSignInfo {
type Error = FastCryptoError;
fn try_from(info: &IotaAuthorityStrongQuorumSignInfo) -> Result<Self, Self::Error> {
Ok(Self {
epoch: info.epoch,
signature: (&info.signature).try_into()?,
signers_map: info.signers_map.clone(),
})
}
}
static_assertions::assert_not_impl_any!(AuthorityStrongQuorumSignInfo: Hash, Eq, PartialEq);
impl<const STRONG_THRESHOLD: bool> AuthoritySignInfoTrait
for AuthorityQuorumSignInfo<STRONG_THRESHOLD>
{
fn verify_secure<T: Serialize>(
&self,
data: &T,
intent: Intent,
committee: &Committee,
) -> IotaResult {
let mut obligation = VerificationObligation::default();
let idx = obligation.add_message(data, self.epoch, intent);
self.add_to_verification_obligation(committee, &mut obligation, idx)?;
obligation.verify_all()?;
Ok(())
}
fn add_to_verification_obligation<'a>(
&self,
committee: &'a Committee,
obligation: &mut VerificationObligation<'a>,
message_index: usize,
) -> IotaResult<()> {
fp_ensure!(self.epoch == committee.epoch(), IotaError::WrongEpoch {
expected_epoch: committee.epoch(),
actual_epoch: self.epoch,
});
let mut weight = 0;
obligation
.signatures
.get_mut(message_index)
.ok_or(IotaError::InvalidAuthenticator)?
.add_aggregate(self.signature.clone())
.map_err(|_| IotaError::InvalidSignature {
error: "Signature Aggregation failed".to_string(),
})?;
let selected_public_keys = obligation
.public_keys
.get_mut(message_index)
.ok_or(IotaError::InvalidAuthenticator)?;
for authority_index in self.signers_map.iter() {
let authority = committee
.authority_by_index(authority_index)
.ok_or_else(|| IotaError::UnknownSigner {
signer: None,
index: Some(authority_index),
committee: Box::new(committee.clone()),
})?;
let voting_rights = committee.weight(authority);
fp_ensure!(voting_rights > 0, IotaError::UnknownSigner {
signer: Some(authority.concise().to_string()),
index: Some(authority_index),
committee: Box::new(committee.clone()),
});
weight += voting_rights;
selected_public_keys.push(committee.public_key(authority)?);
}
fp_ensure!(
weight >= Self::quorum_threshold(committee),
IotaError::CertificateRequiresQuorum
);
Ok(())
}
}
impl<const STRONG_THRESHOLD: bool> AuthorityQuorumSignInfo<STRONG_THRESHOLD> {
pub fn new_from_auth_sign_infos(
auth_sign_infos: Vec<AuthoritySignInfo>,
committee: &Committee,
) -> IotaResult<Self> {
fp_ensure!(
auth_sign_infos.iter().all(|a| a.epoch == committee.epoch),
IotaError::InvalidSignature {
error: "All signatures must be from the same epoch as the committee".to_string()
}
);
let total_stake: StakeUnit = auth_sign_infos
.iter()
.map(|a| committee.weight(&a.authority))
.sum();
fp_ensure!(
total_stake >= Self::quorum_threshold(committee),
IotaError::InvalidSignature {
error: "Signatures don't have enough stake to form a quorum".to_string()
}
);
let signatures: BTreeMap<_, _> = auth_sign_infos
.into_iter()
.map(|a| (a.authority, a.signature))
.collect();
let mut map = RoaringBitmap::new();
for pk in signatures.keys() {
map.insert(
committee
.authority_index(pk)
.ok_or_else(|| IotaError::UnknownSigner {
signer: Some(pk.concise().to_string()),
index: None,
committee: Box::new(committee.clone()),
})?,
);
}
let sigs: Vec<AuthoritySignature> = signatures.into_values().collect();
Ok(AuthorityQuorumSignInfo {
epoch: committee.epoch,
signature: AggregateAuthoritySignature::aggregate(&sigs).map_err(|e| {
IotaError::InvalidSignature {
error: e.to_string(),
}
})?,
signers_map: map,
})
}
pub fn authorities<'a>(
&'a self,
committee: &'a Committee,
) -> impl Iterator<Item = IotaResult<&'a AuthorityName>> {
self.signers_map.iter().map(|i| {
committee
.authority_by_index(i)
.ok_or(IotaError::InvalidAuthenticator)
})
}
pub fn quorum_threshold(committee: &Committee) -> StakeUnit {
committee.threshold::<STRONG_THRESHOLD>()
}
pub fn len(&self) -> u64 {
self.signers_map.len()
}
pub fn is_empty(&self) -> bool {
self.signers_map.is_empty()
}
}
impl<const S: bool> Display for AuthorityQuorumSignInfo<S> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"{} {{ epoch: {:?}, signers_map: {:?} }}",
if S {
"AuthorityStrongQuorumSignInfo"
} else {
"AuthorityWeakQuorumSignInfo"
},
self.epoch,
self.signers_map,
)?;
Ok(())
}
}
mod private {
pub trait SealedAuthoritySignInfoTrait {}
impl SealedAuthoritySignInfoTrait for super::EmptySignInfo {}
impl SealedAuthoritySignInfoTrait for super::AuthoritySignInfo {}
impl<const S: bool> SealedAuthoritySignInfoTrait for super::AuthorityQuorumSignInfo<S> {}
}
pub trait Signable<W> {
fn write(&self, writer: &mut W);
}
pub trait SignableBytes
where
Self: Sized,
{
fn from_signable_bytes(bytes: &[u8]) -> Result<Self, Error>;
}
mod bcs_signable {
pub trait BcsSignable: serde::Serialize + serde::de::DeserializeOwned {}
impl BcsSignable for crate::committee::Committee {}
impl BcsSignable for crate::messages_checkpoint::CheckpointSummary {}
impl BcsSignable for crate::messages_checkpoint::CheckpointContents {}
impl BcsSignable for crate::effects::TransactionEffects {}
impl BcsSignable for crate::effects::TransactionEvents {}
impl BcsSignable for crate::transaction::TransactionData {}
impl BcsSignable for crate::transaction::SenderSignedData {}
impl BcsSignable for crate::object::ObjectInner {}
impl BcsSignable for crate::accumulator::Accumulator {}
impl BcsSignable for super::bcs_signable_test::Foo {}
#[cfg(test)]
impl BcsSignable for super::bcs_signable_test::Bar {}
}
impl<T, W> Signable<W> for T
where
T: bcs_signable::BcsSignable,
W: std::io::Write,
{
fn write(&self, writer: &mut W) {
let name = serde_name::trace_name::<Self>().expect("Self must be a struct or an enum");
write!(writer, "{}::", name).expect("Hasher should not fail");
bcs::serialize_into(writer, &self).expect("Message serialization should not fail");
}
}
impl<W> Signable<W> for EpochId
where
W: std::io::Write,
{
fn write(&self, writer: &mut W) {
bcs::serialize_into(writer, &self).expect("Message serialization should not fail");
}
}
impl<T> SignableBytes for T
where
T: bcs_signable::BcsSignable,
{
fn from_signable_bytes(bytes: &[u8]) -> Result<Self, Error> {
let name = serde_name::trace_name::<Self>().expect("Self should be a struct or an enum");
let name_byte_len = format!("{}::", name).bytes().len();
Ok(bcs::from_bytes(bytes.get(name_byte_len..).ok_or_else(
|| anyhow!("Failed to deserialize to {name}."),
)?)?)
}
}
fn hash<S: Signable<H>, H: HashFunction<DIGEST_SIZE>, const DIGEST_SIZE: usize>(
signable: &S,
) -> [u8; DIGEST_SIZE] {
let mut digest = H::default();
signable.write(&mut digest);
let hash = digest.finalize();
hash.into()
}
pub fn default_hash<S: Signable<DefaultHash>>(signable: &S) -> [u8; 32] {
hash::<S, DefaultHash, 32>(signable)
}
#[derive(Default)]
pub struct VerificationObligation<'a> {
pub messages: Vec<Vec<u8>>,
pub signatures: Vec<AggregateAuthoritySignature>,
pub public_keys: Vec<Vec<&'a AuthorityPublicKey>>,
}
impl<'a> VerificationObligation<'a> {
pub fn new() -> VerificationObligation<'a> {
VerificationObligation::default()
}
pub fn add_message<T>(&mut self, message_value: &T, epoch: EpochId, intent: Intent) -> usize
where
T: Serialize,
{
let intent_msg = IntentMessage::new(intent, message_value);
let mut intent_msg_bytes =
bcs::to_bytes(&intent_msg).expect("Message serialization should not fail");
epoch.write(&mut intent_msg_bytes);
self.signatures.push(AggregateAuthoritySignature::default());
self.public_keys.push(Vec::new());
self.messages.push(intent_msg_bytes);
self.messages.len() - 1
}
pub fn add_signature_and_public_key(
&mut self,
signature: &AuthoritySignature,
public_key: &'a AuthorityPublicKey,
idx: usize,
) -> IotaResult<()> {
self.public_keys
.get_mut(idx)
.ok_or(IotaError::InvalidAuthenticator)?
.push(public_key);
self.signatures
.get_mut(idx)
.ok_or(IotaError::InvalidAuthenticator)?
.add_signature(signature.clone())
.map_err(|_| IotaError::InvalidSignature {
error: "Failed to add signature to obligation".to_string(),
})?;
Ok(())
}
pub fn verify_all(self) -> IotaResult<()> {
let mut pks = Vec::with_capacity(self.public_keys.len());
for pk in self.public_keys.clone() {
pks.push(pk.into_iter());
}
AggregateAuthoritySignature::batch_verify(
&self.signatures.iter().collect::<Vec<_>>()[..],
pks,
&self.messages.iter().map(|x| &x[..]).collect::<Vec<_>>()[..],
)
.map_err(|e| {
let message = format!(
"pks: {:?}, messages: {:?}, sigs: {:?}",
&self.public_keys,
self.messages
.iter()
.map(Base64::encode)
.collect::<Vec<String>>(),
&self
.signatures
.iter()
.map(|s| Base64::encode(s.as_ref()))
.collect::<Vec<String>>()
);
let chunk_size = 2048;
for (i, chunk) in message
.as_bytes()
.chunks(chunk_size)
.map(std::str::from_utf8)
.enumerate()
{
warn!(
"Failed to batch verify aggregated auth sig: {} (chunk {}): {}",
e,
i,
chunk.unwrap()
);
}
IotaError::InvalidSignature {
error: format!("Failed to batch verify aggregated auth sig: {}", e),
}
})?;
Ok(())
}
}
pub mod bcs_signable_test {
use serde::{Deserialize, Serialize};
#[derive(Clone, Serialize, Deserialize)]
pub struct Foo(pub String);
#[cfg(test)]
#[derive(Serialize, Deserialize)]
pub struct Bar(pub String);
#[cfg(test)]
use super::VerificationObligation;
#[cfg(test)]
pub fn get_obligation_input<T>(value: &T) -> (VerificationObligation<'_>, usize)
where
T: super::bcs_signable::BcsSignable,
{
use shared_crypto::intent::{Intent, IntentScope};
let mut obligation = VerificationObligation::default();
let idx = obligation.add_message(
value,
0,
Intent::iota_app(IntentScope::SenderSignedTransaction),
);
(obligation, idx)
}
}
#[derive(
Clone,
Copy,
Deserialize,
Serialize,
JsonSchema,
Debug,
EnumString,
strum_macros::Display,
PartialEq,
Eq,
)]
#[strum(serialize_all = "lowercase")]
pub enum SignatureScheme {
ED25519,
Secp256k1,
Secp256r1,
BLS12381, MultiSig,
ZkLoginAuthenticator,
PasskeyAuthenticator,
}
impl SignatureScheme {
pub fn flag(&self) -> u8 {
match self {
SignatureScheme::ED25519 => 0x00,
SignatureScheme::Secp256k1 => 0x01,
SignatureScheme::Secp256r1 => 0x02,
SignatureScheme::MultiSig => 0x03,
SignatureScheme::BLS12381 => 0x04, SignatureScheme::ZkLoginAuthenticator => 0x05,
SignatureScheme::PasskeyAuthenticator => 0x06,
}
}
pub fn update_hasher_with_flag(&self, hasher: &mut DefaultHash) {
match self {
SignatureScheme::ED25519 => (),
_ => hasher.update([self.flag()]),
};
}
pub fn from_flag(flag: &str) -> Result<SignatureScheme, IotaError> {
let byte_int = flag
.parse::<u8>()
.map_err(|_| IotaError::KeyConversion("Invalid key scheme".to_string()))?;
Self::from_flag_byte(&byte_int)
}
pub fn from_flag_byte(byte_int: &u8) -> Result<SignatureScheme, IotaError> {
match byte_int {
0x00 => Ok(SignatureScheme::ED25519),
0x01 => Ok(SignatureScheme::Secp256k1),
0x02 => Ok(SignatureScheme::Secp256r1),
0x03 => Ok(SignatureScheme::MultiSig),
0x04 => Ok(SignatureScheme::BLS12381),
0x05 => Ok(SignatureScheme::ZkLoginAuthenticator),
0x06 => Ok(SignatureScheme::PasskeyAuthenticator),
_ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub enum CompressedSignature {
Ed25519(Ed25519SignatureAsBytes),
Secp256k1(Secp256k1SignatureAsBytes),
Secp256r1(Secp256r1SignatureAsBytes),
ZkLogin(ZkLoginAuthenticatorAsBytes),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub struct ZkLoginAuthenticatorAsBytes(#[schemars(with = "Base64")] pub Vec<u8>);
impl AsRef<[u8]> for CompressedSignature {
fn as_ref(&self) -> &[u8] {
match self {
CompressedSignature::Ed25519(sig) => &sig.0,
CompressedSignature::Secp256k1(sig) => &sig.0,
CompressedSignature::Secp256r1(sig) => &sig.0,
CompressedSignature::ZkLogin(sig) => &sig.0,
}
}
}
impl FromStr for Signature {
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
}
}
impl FromStr for PublicKey {
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
}
}
impl FromStr for GenericSignature {
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
}
}
pub type RandomnessSignature = fastcrypto_tbls::types::Signature;
pub type RandomnessPartialSignature = fastcrypto_tbls::tbls::PartialSignature<RandomnessSignature>;
pub type RandomnessPrivateKey =
fastcrypto_tbls::ecies::PrivateKey<fastcrypto::groups::bls12381::G2Element>;
#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct RandomnessRound(pub u64);
impl Display for RandomnessRound {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::ops::Add for RandomnessRound {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
impl std::ops::Add<u64> for RandomnessRound {
type Output = Self;
fn add(self, other: u64) -> Self {
Self(self.0 + other)
}
}
impl std::ops::Sub for RandomnessRound {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self(self.0 - other.0)
}
}
impl std::ops::Sub<u64> for RandomnessRound {
type Output = Self;
fn sub(self, other: u64) -> Self {
Self(self.0 - other)
}
}
impl RandomnessRound {
pub fn new(round: u64) -> Self {
Self(round)
}
pub fn checked_add(self, rhs: u64) -> Option<Self> {
self.0.checked_add(rhs).map(Self)
}
pub fn signature_message(&self) -> Vec<u8> {
"random_beacon round "
.as_bytes()
.iter()
.cloned()
.chain(bcs::to_bytes(&self.0).expect("serialization should not fail"))
.collect()
}
}