ghost-node/pallets/exodus/src/exodus.rs
Uncle Stretch d83ee59508
let the EXODUS begin...
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-08-29 14:48:50 +03:00

497 lines
20 KiB
Rust

use crate::{EncryptedMessage, FrostError};
use ghost_networks::NetworkCurve;
use chacha20poly1305::{
aead::{self, Aead, AeadCore, KeyInit},
Key as EncryptionKey,
Nonce as EncryptionNonce,
ChaCha20Poly1305 as EncryptionCipher,
};
use hkdf::Hkdf as KeyDerivation;
use sha2::{Digest, Sha256 as Hashing};
use rand_chacha::rand_core::{CryptoRng, RngCore};
use sp_std::collections::btree_map::BTreeMap;
macro_rules! with_ciphersuite {
($curve:expr, |$alias:ident| $body:block) => {
match $curve {
ExodusCurve::Secp256k1 => {
use frost_secp256k1 as $alias;
$body
},
NetworkCurve::Ed25519 => {
use frost_ed25519 as $alias;
$body
}
}
}
}
impl ExodusCurve {
pub fn dkg_verify_proof_of_knowledge(
&self,
index: u16,
round1_package_bytes: &[u8],
) -> Result<(), FrostError> {
with_ciphersuite!(self, |f| {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
frost_core::keys::dkg::verify_proof_of_knowledge(
identifier,
&round1_package.commitment(),
&round1_package.proof_of_knowledge(),
).map_err(|_| FrostError::InvalidProofOfKnowledge)
})
}
pub fn verify_signature_share(
&self,
index: u16,
verifying_share_bytes: &[u8],
signature_share_bytes: &[u8],
signing_package_bytes: &[u8],
verifying_key_bytes: &[u8],
) -> Result<(), FrostError> {
with_ciphersuite!(self, |f| {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let verifying_share = f::keys::VerifyingShare::deserialize(verifying_share_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signature_share = f::round2::SignatureShare::deserialize(signature_share_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signing_package = f::SigningPackage::deserialize(signing_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let verifying_key = f::VerifyingKey::deserialize(verifying_key_bytes)
.map_err(|_| FrostError::DeserializationError)?;
frost_core::verify_signature_share(
identifier,
&verifying_share,
&signature_share,
&signing_package,
&verifying_key,
).map_err(|err| match err {
f::Error::IdentityCommitment => FrostError::IdentityCommitment,
f::Error::UnknownIdentifier => FrostError::UnknownIdentifier,
f::Error::DuplicatedIdentifier => FrostError::DuplicatedIdentifier,
f::Error::InvalidSignatureShare { .. } => FrostError::InvalidSignatureShare,
_ => FrostError::Unknown,
})
})
}
pub fn aggregate_signature(
&self,
signing_package_bytes: &[u8],
signature_shares_bytes: &BTreeMap<u16, Vec<u8>>,
pubkeys_bytes: &[u8],
) -> Result<Vec<u8>, FrostError> {
with_ciphersuite!(self, |f| {
let signing_package = f::SigningPackage::deserialize(signing_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let mut signature_shares = BTreeMap::new();
for (&index, signature_share_bytes) in signature_shares_bytes.iter() {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let signature_share = f::round2::SignatureShare::deserialize(signature_share_bytes)
.map_err(|_| FrostError::DeserializationError)?;
signature_shares.insert(identifier, signature_share);
}
let pubkeys = f::keys::PublicKeyPackage::deserialize(pubkeys_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signature = f::aggregate(&signing_package, &signature_shares, &pubkeys)
.map_err(|err| match err {
f::Error::UnknownIdentifier => FrostError::UnknownIdentifier,
f::Error::SerializationError => FrostError::SerializationError,
f::Error::IdentityCommitment => FrostError::IdentityCommitment,
f::Error::IncorrectNumberOfIdentifiers => FrostError::IncorrectNumberOfIdentifiers,
f::Error::DuplicatedIdentifier => FrostError::DuplicatedIdentifier,
f::Error::InvalidSecretShare { .. } => FrostError::InvalidSecretShare,
f::Error::InvalidSignature { .. } => FrostError::InvalidSignature,
_ => FrostError::Unknown,
})?;
let signature_bytes = signature.serialize()
.map_err(|_| FrostError::SerializationError)?;
Ok(signature_bytes)
})
}
pub fn is_signature_valid(
&self,
pubkey_bytes: &[u8],
signature_bytes: &[u8],
message: &[u8],
) -> bool {
with_ciphersuite!(self, |f| {
let get_verification_result = || -> Result<(), FrostError> {
let pubkeys = f::keys::PublicKeyPackage::deserialize(pubkey_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signature = f::Signature::deserialize(signature_bytes)
.map_err(|_| FrostError::DeserializationError)?;
pubkeys.verifying_key()
.verify(message, &signature)
.map_err(|_| FrostError::InvalidSignature)?;
Ok(())
};
get_verification_result().is_ok()
})
}
pub fn dkg_part1<R: RngCore + CryptoRng>(
&self,
index: u16,
max_signers: u16,
min_signers: u16,
mut rng: R,
) -> Result<(Vec<u8>, Vec<u8>), FrostError> {
with_ciphersuite!(self, |f| {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let (round1_secret_package, round1_package) = f::keys::dkg::part1(
identifier,
max_signers,
min_signers,
&mut rng,
).map_err(|err| {
match err {
f::Error::InvalidMinSigners => FrostError::InvalidMinSigners(min_signers, max_signers),
f::Error::InvalidMaxSigners => FrostError::InvalidMaxSigners(max_signers),
f::Error::DKGNotSupported => FrostError::DKGNotSupported(*self),
_ => FrostError::Unknown,
}
})?;
let round1_secret_package_bytes = round1_secret_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
let round1_package_bytes = round1_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
Ok((round1_secret_package_bytes, round1_package_bytes))
})
}
fn convert_identifier_to_index(
identifier_bytes: Vec<u8>
) -> Result<u16, FrostError> {
const SIZE: usize = core::mem::size_of::<u16>();
let start = identifier_bytes.len().checked_sub(SIZE)
.ok_or(FrostError::InvalidParticipantId)?;
let bytes_array = identifier_bytes.get(start..)
.and_then(|slice| slice.try_into().ok())
.ok_or(FrostError::InvalidParticipantId)?;
Ok(u16::from_be_bytes(bytes_array))
}
pub fn dkg_part2(
&self,
round1_secret_package_bytes: &[u8],
round1_packages_bytes: &BTreeMap<u16, Vec<u8>>,
) -> Result<(Vec<u8>, BTreeMap<u16, Vec<u8>>), FrostError> {
with_ciphersuite!(self, |f| {
let round1_secret_package = f::keys::dkg::round1::SecretPackage::deserialize(round1_secret_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let mut round1_packages = BTreeMap::new();
for (&index, round1_package_bytes) in round1_packages_bytes.iter() {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
round1_packages.insert(identifier, round1_package);
}
let (round2_secret_package, round2_packages) = f::keys::dkg::part2(
round1_secret_package,
&round1_packages,
).map_err(|err| match err {
f::Error::IncorrectNumberOfCommitments => FrostError::IncorrectNumberOfCommitments,
f::Error::IncorrectNumberOfPackages => FrostError::IncorrectNumberOfPackages,
f::Error::InvalidProofOfKnowledge { .. } => FrostError::InvalidProofOfKnowledge,
_ => FrostError::Unknown,
})?;
let round2_secret_package_bytes = round2_secret_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
let mut round2_packages_bytes = BTreeMap::new();
for (participant_identifier, round2_package) in round2_packages.iter() {
let index = Self::convert_identifier_to_index(
participant_identifier.serialize(),
)?;
let round2_package_bytes = round2_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
round2_packages_bytes.insert(index as u16, round2_package_bytes);
}
Ok((round2_secret_package_bytes, round2_packages_bytes))
})
}
pub fn dkg_encrypt_round2_package<R: RngCore + CryptoRng>(
&self,
cipher: &EncryptionCipher,
associated_data: &[u8],
round2_package_bytes: &[u8],
mut rng: R,
) -> Result<(Vec<u8>, Vec<u8>), FrostError> {
let payload = aead::Payload {
msg: round2_package_bytes,
aad: associated_data,
};
let nonce = EncryptionCipher::generate_nonce(&mut rng);
let ciphertext = cipher.encrypt(&nonce, payload)
.map_err(|_| FrostError::EncryptionFailed)?;
Ok((ciphertext, nonce.to_vec()))
}
pub fn dkg_decrypt_round2_package(
&self,
cipher: &EncryptionCipher,
encrypted_message: &EncryptedMessage,
associated_data: &[u8],
) -> Result<Vec<u8>, FrostError> {
let nonce = EncryptionNonce::from_iter(encrypted_message.nonce.iter().cloned());
let payload = aead::Payload {
msg: &encrypted_message.ciphertext,
aad: associated_data,
};
cipher.decrypt(&nonce, payload)
.map_err(|_| FrostError::DecryptionFailed)
}
pub fn prepare_additional_info(
&self,
sender_index: u16,
receiver_index: u16,
session_index: u16,
) -> Vec<u8> {
let mut additional_info = match self {
ExodusCurve::Secp256k1 => b"EXODUS-SECP256K1-DKG-V1".to_vec(),
};
additional_info.extend_from_slice(&sender_index.to_be_bytes());
additional_info.extend_from_slice(&receiver_index.to_be_bytes());
additional_info.extend_from_slice(&session_index.to_be_bytes());
additional_info
}
pub fn prepare_cipher_from_keys(
&self,
round1_receiver_package_bytes: &[u8],
round1_secret_package_bytes: &[u8],
additional_info: &[u8],
) -> Result<EncryptionCipher, FrostError> {
with_ciphersuite!(self, |f| {
let coefficients =
f::keys::dkg::round1::SecretPackage::deserialize(round1_secret_package_bytes)
.map_err(|_| FrostError::DeserializationError)?
.coefficients();
let sender_secret_key = coefficients
.first()
.ok_or(FrostError::IncorrectNumberOfCoefficients)?;
let receiver_public_key =
f::keys::dkg::round1::Package::deserialize(round1_receiver_package_bytes)
.map_err(|_| FrostError::DeserializationError)?
.commitment()
.coefficients()
.first()
.ok_or(FrostError::MissingCommitment)?
.value();
let shared_secret_bytes = f::VerifyingKey::new(receiver_public_key * sender_secret_key)
.serialize()
.map_err(|_| FrostError::SerializationError)?;
let salt = Hashing::digest(&additional_info);
let hk = KeyDerivation::<Hashing>::new(Some(&salt), &shared_secret_bytes);
let mut encryption_key = EncryptionKey::default();
hk.expand(additional_info, &mut encryption_key)
.map_err(|_| FrostError::HKDFFailed)?;
let cipher = EncryptionCipher::new_from_slice(&encryption_key)
.expect("could not happen; length is already correct qed");
Ok(cipher)
})
}
pub fn dkg_verify_private_package(
&self,
index: u16,
round1_package_bytes: &[u8],
round2_package_bytes: &[u8],
) -> Result<(), FrostError> {
with_ciphersuite!(self, |f| {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let commitment = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
.map(|round1_package| round1_package.commitment().clone())
.map_err(|_| FrostError::DeserializationError)?;
let secret_share = f::keys::dkg::round2::Package::deserialize(round2_package_bytes)
.map(|round2_package| {
let signing_share = round2_package.signing_share().clone();
f::keys::SecretShare::new(identifier, signing_share, commitment)
})
.map_err(|_| FrostError::DeserializationError)?;
let _ = secret_share.verify().map_err(|_| FrostError::InvalidSecretShare)?;
Ok(())
})
}
pub fn dkg_part3(
&self,
round2_secret_package_bytes: &[u8],
round1_packages_bytes: &BTreeMap<u16, Vec<u8>>,
round2_packages_bytes: &BTreeMap<u16, Vec<u8>>,
) -> Result<(Vec<u8>, Vec<u8>), FrostError> {
with_ciphersuite!(self, |f| {
let round2_secret_package = f::keys::dkg::round2::SecretPackage::deserialize(round2_secret_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let mut round1_packages = BTreeMap::new();
for (&index, round1_package_bytes) in round1_packages_bytes.iter() {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let round1_package = f::keys::dkg::round1::Package::deserialize(round1_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
round1_packages.insert(identifier, round1_package);
}
let mut round2_packages = BTreeMap::new();
for (&index, round2_package_bytes) in round2_packages_bytes.iter() {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let round2_package = f::keys::dkg::round2::Package::deserialize(round2_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
round2_packages.insert(identifier, round2_package);
}
let (key_package, pubkey_package) = f::keys::dkg::part3(
&round2_secret_package,
&round1_packages,
&round2_packages,
).map_err(|err| match err {
f::Error::IncorrectNumberOfPackages => FrostError::IncorrectNumberOfPackages,
f::Error::IncorrectPackage => FrostError::IncorrectPackage,
f::Error::PackageNotFound => FrostError::PackageNotFound,
f::Error::InvalidSecretShare { .. } => FrostError::InvalidSecretShare,
f::Error::IncorrectNumberOfCommitments => FrostError::IncorrectNumberOfCommitments,
_ => FrostError::Unknown,
})?;
let key_package_bytes = key_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
let pubkey_package_bytes = pubkey_package.serialize()
.map_err(|_| FrostError::SerializationError)?;
Ok((key_package_bytes, pubkey_package_bytes))
})
}
pub fn generate_nonce<R: CryptoRng + RngCore>(
&self,
secret_key_package_bytes: &[u8],
mut rng: R
) -> Result<(Vec<u8>, Vec<u8>), FrostError> {
with_ciphersuite!(self, |f| {
let signing_share = f::keys::KeyPackage::deserialize(secret_key_package_bytes)
.map(|key_package| *key_package.signing_share())
.map_err(|_| FrostError::DeserializationError)?;
let (signing_nonces, signing_commitments) =
f::round1::commit(&signing_share, &mut rng);
let signing_nonces_bytes = signing_nonces.serialize()
.map_err(|_| FrostError::SerializationError)?;
let signing_commitments_bytes = signing_commitments.serialize()
.map_err(|_| FrostError::SerializationError)?;
Ok((signing_nonces_bytes, signing_commitments_bytes))
})
}
pub fn generate_signing_package(
&self,
commitments_map_bytes: &BTreeMap<u16, Vec<u8>>,
message: &[u8],
) -> Result<Vec<u8>, FrostError> {
with_ciphersuite!(self, |f| {
let mut commitments_map = BTreeMap::new();
for (&index, commitment_bytes) in commitments_map_bytes.iter() {
let identifier = index.try_into().map_err(|_| FrostError::InvalidParticipantId)?;
let commitment = f::round1::SigningCommitments::deserialize(commitment_bytes)
.map_err(|_| FrostError::DeserializationError)?;
commitments_map.insert(identifier, commitment);
}
f::SigningPackage::new(commitments_map, message)
.serialize()
.map_err(|_| FrostError::SerializationError)
})
}
pub fn sign_message(
&self,
signing_package_bytes: &[u8],
signer_nonces_bytes: &[u8],
secret_key_share_bytes: &[u8]
) -> Result<Vec<u8>, FrostError> {
with_ciphersuite!(self, |f| {
let signing_package = f::SigningPackage::deserialize(signing_package_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signer_nonces = f::round1::SigningNonces::deserialize(signer_nonces_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let secret_key_share = f::keys::KeyPackage::deserialize(secret_key_share_bytes)
.map_err(|_| FrostError::DeserializationError)?;
let signature_share_bytes = f::round2::sign(&signing_package, &signer_nonces, &secret_key_share)
.map(|signature_share| signature_share.serialize())
.map_err(|err| match err {
f::Error::IncorrectNumberOfCommitments => FrostError::IncorrectNumberOfCommitments,
f::Error::MissingCommitment => FrostError::MissingCommitment,
f::Error::IncorrectCommitment => FrostError::IncorrectCommitment,
f::Error::SerializationError => FrostError::SerializationError,
f::Error::UnknownIdentifier => FrostError::UnknownIdentifier,
f::Error::IdentityCommitment => FrostError::IdentityCommitment,
f::Error::DuplicatedIdentifier => FrostError::DuplicatedIdentifier,
f::Error::IncorrectNumberOfIdentifiers => FrostError::IncorrectNumberOfIdentifiers,
_ => FrostError::Unknown,
})?;
Ok(signature_share_bytes)
})
}
}