use crate::signing::{KeyPair, PublicKey, verify as verify_signature};
use blake3;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum LinkableRingError {
#[error("Ring must contain at least 2 public keys")]
RingTooSmall,
#[error("Signer not found in ring")]
SignerNotInRing,
#[error("Invalid signature")]
InvalidSignature,
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Ring size mismatch")]
RingSizeMismatch,
#[error("Invalid key image")]
InvalidKeyImage,
}
pub type LinkableRingResult<T> = Result<T, LinkableRingError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkableRingSignature {
commitments: Vec<[u8; 32]>,
#[serde(with = "serde_signature")]
signature: [u8; 64],
key_image: [u8; 32],
ring_hash: [u8; 32],
}
mod serde_signature {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &[u8; 64], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(bytes)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
where
D: Deserializer<'de>,
{
let bytes: Vec<u8> = Deserialize::deserialize(deserializer)?;
if bytes.len() != 64 {
return Err(serde::de::Error::custom("invalid signature length"));
}
let mut array = [0u8; 64];
array.copy_from_slice(&bytes);
Ok(array)
}
}
impl LinkableRingSignature {
pub fn to_bytes(&self) -> LinkableRingResult<Vec<u8>> {
crate::codec::encode(self).map_err(|e| LinkableRingError::SerializationError(e.to_string()))
}
pub fn from_bytes(bytes: &[u8]) -> LinkableRingResult<Self> {
crate::codec::decode(bytes)
.map_err(|e| LinkableRingError::SerializationError(e.to_string()))
}
pub fn key_image(&self) -> &[u8; 32] {
&self.key_image
}
pub fn ring_hash(&self) -> &[u8; 32] {
&self.ring_hash
}
pub fn ring_size(&self) -> usize {
self.commitments.len()
}
}
fn compute_key_image(secret_key: &[u8; 32], ring_hash: &[u8; 32]) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(b"CHIE-KEY-IMAGE-V1");
hasher.update(secret_key);
hasher.update(ring_hash);
*hasher.finalize().as_bytes()
}
fn compute_ring_hash(ring: &[PublicKey]) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(b"CHIE-RING-HASH-V1");
for pk in ring {
hasher.update(pk);
}
*hasher.finalize().as_bytes()
}
pub fn sign_linkable(
signer: &KeyPair,
ring: &[PublicKey],
message: &[u8],
) -> LinkableRingResult<LinkableRingSignature> {
if ring.len() < 2 {
return Err(LinkableRingError::RingTooSmall);
}
let signer_pubkey = signer.public_key();
let _signer_index = ring
.iter()
.position(|pk| pk == &signer_pubkey)
.ok_or(LinkableRingError::SignerNotInRing)?;
let ring_hash = compute_ring_hash(ring);
let secret_key = signer.secret_key();
let key_image = compute_key_image(&secret_key, &ring_hash);
let mut commitments = Vec::with_capacity(ring.len());
for (i, pk) in ring.iter().enumerate() {
let mut hasher = blake3::Hasher::new();
hasher.update(b"CHIE-LINKABLE-RING-V1");
hasher.update(message);
hasher.update(pk);
hasher.update(&i.to_le_bytes());
hasher.update(&ring_hash);
hasher.update(&key_image);
for ring_pk in ring {
hasher.update(ring_pk);
}
commitments.push(*hasher.finalize().as_bytes());
}
let mut sig_message = Vec::new();
sig_message.extend_from_slice(message);
sig_message.extend_from_slice(&key_image);
sig_message.extend_from_slice(&ring_hash);
for commitment in &commitments {
sig_message.extend_from_slice(commitment);
}
let signature = signer.sign(&sig_message);
Ok(LinkableRingSignature {
commitments,
signature,
key_image,
ring_hash,
})
}
pub fn verify_linkable(
ring: &[PublicKey],
message: &[u8],
signature: &LinkableRingSignature,
) -> LinkableRingResult<bool> {
if ring.len() < 2 {
return Err(LinkableRingError::RingTooSmall);
}
if ring.len() != signature.commitments.len() {
return Err(LinkableRingError::RingSizeMismatch);
}
let expected_ring_hash = compute_ring_hash(ring);
if expected_ring_hash != signature.ring_hash {
return Ok(false);
}
for (i, pk) in ring.iter().enumerate() {
let mut hasher = blake3::Hasher::new();
hasher.update(b"CHIE-LINKABLE-RING-V1");
hasher.update(message);
hasher.update(pk);
hasher.update(&i.to_le_bytes());
hasher.update(&signature.ring_hash);
hasher.update(&signature.key_image);
for ring_pk in ring {
hasher.update(ring_pk);
}
let expected_commitment = hasher.finalize();
if expected_commitment.as_bytes() != &signature.commitments[i] {
return Ok(false);
}
}
let mut sig_message = Vec::new();
sig_message.extend_from_slice(message);
sig_message.extend_from_slice(&signature.key_image);
sig_message.extend_from_slice(&signature.ring_hash);
for commitment in &signature.commitments {
sig_message.extend_from_slice(commitment);
}
for pk in ring {
if verify_signature(pk, &sig_message, &signature.signature).is_ok() {
return Ok(true);
}
}
Ok(false)
}
pub fn check_double_sign(sig1: &LinkableRingSignature, sig2: &LinkableRingSignature) -> bool {
sig1.key_image == sig2.key_image && sig1.ring_hash == sig2.ring_hash
}
pub struct KeyImageDb {
used_images: std::collections::HashSet<Vec<u8>>,
}
impl KeyImageDb {
pub fn new() -> Self {
Self {
used_images: std::collections::HashSet::new(),
}
}
pub fn is_used(&self, signature: &LinkableRingSignature) -> bool {
let mut key = Vec::new();
key.extend_from_slice(&signature.ring_hash);
key.extend_from_slice(&signature.key_image);
self.used_images.contains(&key)
}
pub fn mark_used(&mut self, signature: &LinkableRingSignature) -> bool {
let mut key = Vec::new();
key.extend_from_slice(&signature.ring_hash);
key.extend_from_slice(&signature.key_image);
self.used_images.insert(key)
}
pub fn size(&self) -> usize {
self.used_images.len()
}
pub fn clear(&mut self) {
self.used_images.clear();
}
}
impl Default for KeyImageDb {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::signing::KeyPair;
#[test]
fn test_linkable_ring_basic() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let keypair3 = KeyPair::generate();
let ring = vec![
keypair1.public_key(),
keypair2.public_key(),
keypair3.public_key(),
];
let message = b"Test message";
let signature = sign_linkable(&keypair2, &ring, message).unwrap();
assert!(verify_linkable(&ring, message, &signature).unwrap());
}
#[test]
fn test_double_sign_detection() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let keypair3 = KeyPair::generate();
let ring = vec![
keypair1.public_key(),
keypair2.public_key(),
keypair3.public_key(),
];
let msg1 = b"Transaction 1";
let msg2 = b"Transaction 2";
let sig1 = sign_linkable(&keypair2, &ring, msg1).unwrap();
let sig2 = sign_linkable(&keypair2, &ring, msg2).unwrap();
assert!(check_double_sign(&sig1, &sig2));
assert_eq!(sig1.key_image(), sig2.key_image());
}
#[test]
fn test_different_signers_different_images() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let keypair3 = KeyPair::generate();
let ring = vec![
keypair1.public_key(),
keypair2.public_key(),
keypair3.public_key(),
];
let message = b"Same message";
let sig1 = sign_linkable(&keypair1, &ring, message).unwrap();
let sig2 = sign_linkable(&keypair2, &ring, message).unwrap();
assert!(!check_double_sign(&sig1, &sig2));
assert_ne!(sig1.key_image(), sig2.key_image());
}
#[test]
fn test_different_rings_different_images() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let keypair3 = KeyPair::generate();
let keypair4 = KeyPair::generate();
let ring1 = vec![
keypair1.public_key(),
keypair2.public_key(),
keypair3.public_key(),
];
let ring2 = vec![
keypair1.public_key(),
keypair2.public_key(),
keypair4.public_key(),
];
let message = b"Test";
let sig1 = sign_linkable(&keypair1, &ring1, message).unwrap();
let sig2 = sign_linkable(&keypair1, &ring2, message).unwrap();
assert!(!check_double_sign(&sig1, &sig2));
assert_ne!(sig1.key_image(), sig2.key_image());
assert_ne!(sig1.ring_hash(), sig2.ring_hash());
}
#[test]
fn test_serialization() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let ring = vec![keypair1.public_key(), keypair2.public_key()];
let message = b"Serialization test";
let signature = sign_linkable(&keypair1, &ring, message).unwrap();
let bytes = signature.to_bytes().unwrap();
let deserialized = LinkableRingSignature::from_bytes(&bytes).unwrap();
assert!(verify_linkable(&ring, message, &deserialized).unwrap());
assert_eq!(signature.key_image(), deserialized.key_image());
}
#[test]
fn test_key_image_db() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let ring = vec![keypair1.public_key(), keypair2.public_key()];
let msg1 = b"Transaction 1";
let msg2 = b"Transaction 2";
let sig1 = sign_linkable(&keypair1, &ring, msg1).unwrap();
let sig2 = sign_linkable(&keypair1, &ring, msg2).unwrap();
let mut db = KeyImageDb::new();
assert!(db.mark_used(&sig1));
assert_eq!(db.size(), 1);
assert!(db.is_used(&sig1));
assert!(!db.mark_used(&sig2));
let sig3 = sign_linkable(&keypair2, &ring, msg1).unwrap();
assert!(db.mark_used(&sig3));
assert_eq!(db.size(), 2);
}
#[test]
fn test_wrong_message() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let ring = vec![keypair1.public_key(), keypair2.public_key()];
let message = b"Original";
let wrong_message = b"Wrong";
let signature = sign_linkable(&keypair1, &ring, message).unwrap();
assert!(!verify_linkable(&ring, wrong_message, &signature).unwrap());
}
#[test]
fn test_ring_too_small() {
let keypair = KeyPair::generate();
let ring = vec![keypair.public_key()];
let message = b"Test";
let result = sign_linkable(&keypair, &ring, message);
assert!(matches!(result, Err(LinkableRingError::RingTooSmall)));
}
#[test]
fn test_signer_not_in_ring() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let outsider = KeyPair::generate();
let ring = vec![keypair1.public_key(), keypair2.public_key()];
let message = b"Test";
let result = sign_linkable(&outsider, &ring, message);
assert!(matches!(result, Err(LinkableRingError::SignerNotInRing)));
}
}