use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
use frost_core_unofficial::round1::Nonce;
use frost_core_unofficial::round1::NonceCommitment;
use frost_secp256k1_tr_unofficial::keys::EvenY;
use frost_secp256k1_tr_unofficial::keys::KeyPackage as FrostKeyPackage;
use frost_secp256k1_tr_unofficial::keys::PublicKeyPackage;
use frost_secp256k1_tr_unofficial::keys::SigningShare;
use frost_secp256k1_tr_unofficial::keys::Tweak;
use frost_secp256k1_tr_unofficial::keys::VerifyingShare;
use frost_secp256k1_tr_unofficial::round1::SigningCommitments as FrostSigningCommitments;
use frost_secp256k1_tr_unofficial::round1::SigningNonces as FrostSigningNonces;
use frost_secp256k1_tr_unofficial::round2::SignatureShare;
use frost_secp256k1_tr_unofficial::Identifier;
use frost_secp256k1_tr_unofficial::SigningPackage;
use frost_secp256k1_tr_unofficial::VerifyingKey;
use spark_protos::common::*;
use spark_protos::frost::*;
use crate::error::FrostOperationError;
use crate::error::InvalidCommitmentError;
use crate::error::InvalidIdentifierError;
use crate::error::InvalidKeyError;
use crate::error::InvalidKeyPackageError;
use crate::error::InvalidNonceError;
use crate::error::InvalidRoleError;
use crate::error::InvalidSignatureShareError;
use crate::error::SparkCryptographyError;
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_nonce_from_proto(
nonce: &SigningNonce,
) -> Result<FrostSigningNonces, SparkCryptographyError> {
let hiding_bytes = nonce.hiding.as_slice();
let binding_bytes = nonce.binding.as_slice();
let hiding = Nonce::deserialize(hiding_bytes)
.map_err(|_| SparkCryptographyError::InvalidNonce(InvalidNonceError::InvalidFormat))?;
let binding = Nonce::deserialize(binding_bytes)
.map_err(|_| SparkCryptographyError::InvalidNonce(InvalidNonceError::InvalidFormat))?;
Ok(FrostSigningNonces::from_nonces(hiding, binding))
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_commitments_from_proto(
commitments: &SigningCommitment,
) -> Result<FrostSigningCommitments, SparkCryptographyError> {
let hiding_bytes = commitments.hiding.as_slice();
let binding_bytes = commitments.binding.as_slice();
let hiding_commitment = NonceCommitment::deserialize(hiding_bytes).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::InvalidFormat)
})?;
let binding_commitment = NonceCommitment::deserialize(binding_bytes).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::InvalidFormat)
})?;
Ok(FrostSigningCommitments::new(
hiding_commitment,
binding_commitment,
))
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_signing_commitments_map_from_proto(
map: &HashMap<String, SigningCommitment>,
) -> Result<BTreeMap<Identifier, FrostSigningCommitments>, SparkCryptographyError> {
map.iter()
.map(
|(k, v)| -> Result<(Identifier, FrostSigningCommitments), SparkCryptographyError> {
let identifier = hex_string_to_identifier(k).map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError)
})?;
let commitments = frost_commitments_from_proto(v)?;
Ok((identifier, commitments))
},
)
.collect::<Result<BTreeMap<_, _>, SparkCryptographyError>>()
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn verifying_key_from_bytes(bytes: Vec<u8>) -> Result<VerifyingKey, SparkCryptographyError> {
VerifyingKey::deserialize(bytes.as_slice())
.map_err(|_| SparkCryptographyError::InvalidKey(InvalidKeyError::InvalidVerifyingKey))
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_build_signin_package(
signing_commitments: BTreeMap<Identifier, FrostSigningCommitments>,
message: &[u8],
signing_participants_groups: Option<Vec<BTreeSet<Identifier>>>,
adaptor_public_key: &[u8],
) -> SigningPackage {
let adaptor_public_key = VerifyingKey::deserialize(adaptor_public_key).ok();
SigningPackage::new_with_adaptor(
signing_commitments,
signing_participants_groups,
message,
adaptor_public_key,
)
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_signature_shares_from_proto(
shares: &HashMap<String, Vec<u8>>,
user_identifier: Identifier,
user_signature_share: &Vec<u8>,
) -> Result<BTreeMap<Identifier, SignatureShare>, SparkCryptographyError> {
let mut shares_map = shares
.iter()
.map(
|(k, v)| -> Result<(Identifier, SignatureShare), SparkCryptographyError> {
let identifier = hex_string_to_identifier(k).map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError)
})?;
let share = SignatureShare::deserialize(v).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?;
Ok((identifier, share))
},
)
.collect::<Result<BTreeMap<_, _>, SparkCryptographyError>>()?;
if !user_signature_share.is_empty() {
shares_map.insert(
user_identifier,
SignatureShare::deserialize(user_signature_share).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?,
);
}
Ok(shares_map)
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_public_package_from_proto(
public_shares: &HashMap<String, Vec<u8>>,
user_identifier: Identifier,
user_public_key: Vec<u8>,
verifying_key: VerifyingKey,
) -> Result<PublicKeyPackage, SparkCryptographyError> {
let mut final_shares = public_shares
.iter()
.map(
|(k, v)| -> Result<(Identifier, VerifyingShare), SparkCryptographyError> {
let identifier = hex_string_to_identifier(k).map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError)
})?;
let share = VerifyingShare::deserialize(v).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?;
Ok((identifier, share))
},
)
.collect::<Result<BTreeMap<_, _>, SparkCryptographyError>>()?;
if !user_public_key.is_empty() {
final_shares.insert(
user_identifier,
VerifyingShare::deserialize(user_public_key.as_slice()).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?,
);
}
let public_key_package = PublicKeyPackage::new(final_shares, verifying_key);
Ok(public_key_package)
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_key_package_from_proto(
key_package: &KeyPackage,
identifier_override: Option<Identifier>,
verifying_key: VerifyingKey,
role: i32,
) -> Result<FrostKeyPackage, SparkCryptographyError> {
let signing_share =
SigningShare::deserialize(key_package.secret_share.as_slice()).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::InvalidFormat)
})?;
let verifying_share = VerifyingShare::deserialize(
key_package
.public_shares
.get(&key_package.identifier)
.ok_or(SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
))?
.as_slice(),
)
.map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::InvalidFormat)
})?;
let identifier =
identifier_override.unwrap_or(hex_string_to_identifier(&key_package.identifier).map_err(
|_| SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError),
)?);
let result = FrostKeyPackage::new(
identifier,
signing_share,
verifying_share,
verifying_key,
key_package.min_signers as u16,
);
if role == 1 {
let merkle_root = vec![];
let result_tweaked = result.clone().tweak(Some(&merkle_root));
let result_even_y = result.clone().into_even_y(Some(verifying_key.has_even_y()));
let final_result = FrostKeyPackage::new(
*result_even_y.identifier(),
*result_even_y.signing_share(),
*result_even_y.verifying_share(),
*result_tweaked.verifying_key(),
*result_tweaked.min_signers(),
);
Ok(final_result)
} else {
Ok(result)
}
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn frost_nonce(req: &FrostNonceRequest) -> Result<FrostNonceResponse, SparkCryptographyError> {
let mut results = Vec::new();
for key_package in req.key_packages.iter() {
let verifying_key = verifying_key_from_bytes(key_package.public_key.clone())?;
let key_package = frost_key_package_from_proto(key_package, None, verifying_key, 0)
.map_err(|_| {
SparkCryptographyError::InvalidKeyPackage(InvalidKeyPackageError::InvalidContent)
})?;
let rng = &mut rand::thread_rng();
let (nonce, commitment) =
frost_secp256k1_tr_unofficial::round1::commit(key_package.signing_share(), rng);
let pb_nonce = SigningNonce {
hiding: nonce.hiding().serialize().to_vec(),
binding: nonce.binding().serialize().to_vec(),
};
let pb_commitment = SigningCommitment {
hiding: commitment.hiding().serialize().map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::InvalidFormat)
})?,
binding: commitment.binding().serialize().map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::InvalidFormat)
})?,
};
results.push(SigningNonceResult {
nonces: Some(pb_nonce),
commitments: Some(pb_commitment),
});
}
Ok(FrostNonceResponse { results })
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn sign_frost(req: &SignFrostRequest) -> Result<SignFrostResponse, SparkCryptographyError> {
let mut results = HashMap::new();
for job in req.signing_jobs.iter() {
let mut commitments =
frost_signing_commitments_map_from_proto(&job.commitments).map_err(|_| {
SparkCryptographyError::InvalidCommitment(
InvalidCommitmentError::InvalidCommitmentMap,
)
})?;
let user_identifier =
Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
let mut signing_participants_groups = Vec::new();
signing_participants_groups.push(commitments.keys().cloned().collect());
if let Some(c) = &job.user_commitments {
let user_commitments = frost_commitments_from_proto(c).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::ParseError)
})?;
commitments.insert(user_identifier, user_commitments);
signing_participants_groups.push(BTreeSet::from([user_identifier]));
};
let nonce = match &job.nonce {
Some(nonce) => frost_nonce_from_proto(nonce)
.map_err(|_| SparkCryptographyError::InvalidNonce(InvalidNonceError::ParseError))?,
None => {
return Err(SparkCryptographyError::InvalidNonce(
InvalidNonceError::InvalidFormat,
))
}
};
let verifying_key = verifying_key_from_bytes(job.verifying_key.clone())?;
let identifier_override = match req.role {
0 => None,
1 => Some(user_identifier),
_ => {
return Err(SparkCryptographyError::InvalidRole(
InvalidRoleError::UnsupportedRole,
))
}
};
let key_package = match &job.key_package {
Some(key_package) => frost_key_package_from_proto(
key_package,
identifier_override,
verifying_key,
req.role,
)
.map_err(|_| {
SparkCryptographyError::InvalidKeyPackage(InvalidKeyPackageError::InvalidContent)
})?,
None => {
return Err(SparkCryptographyError::InvalidKeyPackage(
InvalidKeyPackageError::MissingKeyPackage,
))
}
};
let signing_package = frost_build_signin_package(
commitments,
&job.message,
Some(signing_participants_groups),
&job.adaptor_public_key,
);
let tweak = vec![];
let signature_share = match req.role {
0 => frost_secp256k1_tr_unofficial::round2::sign_with_tweak(
&signing_package,
&nonce,
&key_package,
Some(tweak.as_slice()),
)
.map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?,
_ => {
frost_secp256k1_tr_unofficial::round2::sign(&signing_package, &nonce, &key_package)
.map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::InvalidFormat,
)
})?
}
};
results.insert(
job.job_id.clone(),
SigningResult {
signature_share: signature_share.serialize().to_vec(),
},
);
}
Ok(SignFrostResponse { results })
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn aggregate_frost(
req: &AggregateFrostRequest,
) -> Result<AggregateFrostResponse, SparkCryptographyError> {
let mut commitments =
frost_signing_commitments_map_from_proto(&req.commitments).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::ParseError)
})?;
let mut signing_participants_groups = Vec::new();
signing_participants_groups.push(commitments.keys().cloned().collect());
let user_identifier =
Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
if let Some(c) = &req.user_commitments {
let user_commitments = frost_commitments_from_proto(c).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::ParseError)
})?;
commitments.insert(user_identifier, user_commitments);
};
let verifying_key = verifying_key_from_bytes(req.verifying_key.clone())?;
signing_participants_groups.push(BTreeSet::from([user_identifier]));
let signing_package = frost_build_signin_package(
commitments,
&req.message,
Some(signing_participants_groups),
&req.adaptor_public_key,
);
let signature_shares = frost_signature_shares_from_proto(
&req.signature_shares,
user_identifier,
&req.user_signature_share,
)
.map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::ParseError)
})?;
let public_package = frost_public_package_from_proto(
&req.public_shares,
user_identifier,
req.user_public_key.clone(),
verifying_key,
)
.map_err(|_| SparkCryptographyError::FrostOperation(FrostOperationError::ParseError))?;
let tweak = vec![];
let signature = frost_secp256k1_tr_unofficial::aggregate_with_tweak(
&signing_package,
&signature_shares,
&public_package,
Some(&tweak),
)
.map_err(|_| SparkCryptographyError::FrostOperation(FrostOperationError::AggregationError))?;
Ok(AggregateFrostResponse {
signature: signature.serialize().map_err(|_| {
SparkCryptographyError::FrostOperation(FrostOperationError::SerializationError)
})?,
})
}
#[cfg_attr(feature = "telemetry", tracing::instrument(skip_all))]
pub fn validate_signature_share(
req: &ValidateSignatureShareRequest,
) -> Result<(), SparkCryptographyError> {
let identifier = match req.role {
0 => hex_string_to_identifier(&req.identifier).map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError)
})?,
1 => Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier"),
_ => {
return Err(SparkCryptographyError::InvalidRole(
InvalidRoleError::UnsupportedRole,
))
}
};
let signature_share =
SignatureShare::deserialize(req.signature_share.as_slice()).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::InvalidFormat)
})?;
let verifying_key = verifying_key_from_bytes(req.verifying_key.clone())?;
let mut commitments =
frost_signing_commitments_map_from_proto(&req.commitments).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::ParseError)
})?;
let user_identifier =
Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
let mut signing_participants_groups = Vec::new();
signing_participants_groups.push(commitments.keys().cloned().collect());
if let Some(c) = &req.user_commitments {
let user_commitments = frost_commitments_from_proto(c).map_err(|_| {
SparkCryptographyError::InvalidCommitment(InvalidCommitmentError::ParseError)
})?;
commitments.insert(user_identifier, user_commitments);
signing_participants_groups.push(BTreeSet::from([user_identifier]));
}
let public_share = VerifyingShare::deserialize(req.public_share.as_slice()).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::InvalidFormat)
})?;
let adaptor_key: Vec<u8> = vec![];
let signing_package = frost_build_signin_package(
commitments,
&req.message,
Some(signing_participants_groups),
&adaptor_key,
);
let dummy_signing_share = SigningShare::deserialize(&[0; 32]).map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(InvalidSignatureShareError::InvalidFormat)
})?;
let result = FrostKeyPackage::new(
identifier,
dummy_signing_share,
public_share,
verifying_key,
2,
);
let merkle_root = vec![];
let result_tweaked = result.clone().tweak(Some(&merkle_root));
let result_even_y = result.clone().into_even_y(Some(verifying_key.has_even_y()));
let verifying_share = match req.role {
0 => result_tweaked.verifying_share(),
1 => result_even_y.verifying_share(),
_ => {
return Err(SparkCryptographyError::InvalidRole(
InvalidRoleError::UnsupportedRole,
))
}
};
let verify_identifier = match req.role {
0 => identifier,
1 => user_identifier,
_ => {
return Err(SparkCryptographyError::InvalidRole(
InvalidRoleError::UnsupportedRole,
))
}
};
frost_secp256k1_tr_unofficial::verify_signature_share(
verify_identifier,
verifying_share,
&signature_share,
&signing_package,
result_tweaked.verifying_key(),
)
.map_err(|_| {
SparkCryptographyError::InvalidSignatureShare(
InvalidSignatureShareError::VerificationFailed,
)
})?;
Ok(())
}
pub fn hex_string_to_identifier(identifier: &str) -> Result<Identifier, SparkCryptographyError> {
let id_bytes: [u8; 32] = hex::decode(identifier)
.map_err(|_| SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::ParseError))?
.try_into()
.map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::InvalidLength)
})?;
Identifier::deserialize(&id_bytes).map_err(|_| {
SparkCryptographyError::InvalidIdentifier(InvalidIdentifierError::InvalidFormat)
})
}
pub fn identifier_to_hex_string(identifier: &Identifier) -> String {
hex::encode(identifier.serialize())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frost_nonce_from_proto() {
let valid_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![0; 32],
binding: vec![0; 32],
});
let empty_hiding_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![],
binding: vec![0; 32],
});
let empty_binding_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![0; 32],
binding: vec![],
});
let empty_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![],
binding: vec![],
});
let invalid_hiding_length_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![0; 31],
binding: vec![0; 32],
});
let invalid_binding_length_result = frost_nonce_from_proto(&SigningNonce {
hiding: vec![0; 32],
binding: vec![0; 31],
});
assert!(valid_result.is_ok());
assert!(empty_hiding_result.is_err());
assert!(empty_binding_result.is_err());
assert!(invalid_hiding_length_result.is_err());
assert!(invalid_binding_length_result.is_err());
assert!(empty_result.is_err());
}
#[test]
fn test_frost_commitments_from_proto() {
let rng = &mut rand::thread_rng();
let dummy_signing_share = SigningShare::deserialize(&[0; 32]).unwrap();
let (_, commitment) =
frost_secp256k1_tr_unofficial::round1::commit(&dummy_signing_share, rng);
let valid_result = frost_commitments_from_proto(&SigningCommitment {
hiding: commitment.hiding().serialize().unwrap(),
binding: commitment.binding().serialize().unwrap(),
});
let nonscalar_result = frost_commitments_from_proto(&SigningCommitment {
hiding: vec![0; 32],
binding: vec![0; 32],
});
let empty_hiding_result = frost_commitments_from_proto(&SigningCommitment {
hiding: vec![],
binding: vec![0; 32],
});
let empty_binding_result = frost_commitments_from_proto(&SigningCommitment {
hiding: vec![0; 32],
binding: vec![],
});
let invalid_hiding_length_result = frost_commitments_from_proto(&SigningCommitment {
hiding: vec![0; 31],
binding: vec![0; 32],
});
let invalid_binding_length_result = frost_commitments_from_proto(&SigningCommitment {
hiding: vec![0; 32],
binding: vec![0; 31],
});
assert!(valid_result.is_ok());
assert!(nonscalar_result.is_err());
assert!(empty_hiding_result.is_err());
assert!(empty_binding_result.is_err());
assert!(invalid_hiding_length_result.is_err());
assert!(invalid_binding_length_result.is_err());
}
#[test]
fn test_verifying_key_from_bytes() {
let rng = &mut rand::thread_rng();
let dummy_signing_share = SigningShare::deserialize(&[0; 32]).unwrap();
let (_, commitment) =
frost_secp256k1_tr_unofficial::round1::commit(&dummy_signing_share, rng);
let valid_result = verifying_key_from_bytes(commitment.hiding().serialize().unwrap());
let invalid_result = verifying_key_from_bytes(vec![0; 32]);
let invalid_length_result = verifying_key_from_bytes(vec![0; 31]);
let empty_result = verifying_key_from_bytes(vec![]);
assert!(valid_result.is_ok());
assert!(invalid_result.is_err());
assert!(invalid_length_result.is_err());
assert!(empty_result.is_err());
}
}