zrtp 0.1.0

RFC 6189 ZRTP protocol core, crypto helpers, and embeddable engine
Documentation
use std::collections::VecDeque;
use zrtp::*;

#[derive(Default)]
struct MemoryLink {
    queue: VecDeque<QueuedPacket>,
}

enum QueuedPacket {
    ToA(Vec<u8>),
    ToB(Vec<u8>),
}

impl MemoryLink {
    fn send(&mut self, from_a: bool, packet: Packet) {
        let bytes = packet.encode();
        if from_a {
            self.queue.push_back(QueuedPacket::ToB(bytes));
        } else {
            self.queue.push_back(QueuedPacket::ToA(bytes));
        }
    }

    fn pop(&mut self) -> Option<(bool, Packet)> {
        match self.queue.pop_front()? {
            QueuedPacket::ToA(bytes) => {
                Some((false, Packet::decode(&bytes).expect("valid ZRTP packet")))
            }
            QueuedPacket::ToB(bytes) => {
                Some((true, Packet::decode(&bytes).expect("valid ZRTP packet")))
            }
        }
    }
}

struct Peer {
    is_a: bool,
    sequence: u16,
    ssrc: u32,
    engine: ZrtpEngine<MemorySharedSecretStore>,
}

impl Peer {
    fn new(is_a: bool, role: Role, hello: Hello) -> Self {
        Self {
            is_a,
            sequence: if is_a { 1 } else { 100 },
            ssrc: if is_a { 0x1111_2222 } else { 0x3333_4444 },
            engine: ZrtpEngine::new(role, hello, MemorySharedSecretStore::default()),
        }
    }

    fn emit(&mut self, message: Message, link: &mut MemoryLink) {
        let packet = Packet {
            sequence: self.sequence,
            ssrc: self.ssrc,
            message,
        };
        self.sequence = self.sequence.wrapping_add(1);
        link.send(self.is_a, packet);
    }

    fn handle_packet(&mut self, packet: Packet, link: &mut MemoryLink) {
        let events = self
            .engine
            .receive(&packet.message)
            .expect("ZRTP state transition should succeed");
        for event in events {
            match event {
                EngineEvent::Send(message) => self.emit(message, link),
                EngineEvent::Negotiated(algos) => println!("negotiated: {:?}", algos),
                EngineEvent::SasReady(sas) => println!("SAS ready: {sas}"),
                EngineEvent::SecureOn => println!("secure channel established"),
                EngineEvent::SecureOff => println!("secure channel cleared"),
                EngineEvent::Error(code) => println!("error: {:?}", code),
            }
        }
    }
}

fn hello(zid: [u8; 12], client_id: [u8; 16]) -> Hello {
    Hello {
        version: VERSION_1_10,
        client_id,
        hash_image_h3: [0; 32],
        zid,
        signature_capable: false,
        mitm_capable: false,
        passive_capable: false,
        hashes: vec![algos::HASH_S256],
        ciphers: vec![algos::CIPHER_AES1],
        auth_tags: vec![algos::AUTH_HS32],
        key_agreements: vec![algos::KEYAGREE_EC25],
        sas_types: vec![algos::SAS_B32],
        mac: [0; 8],
    }
}

fn main() {
    let mut link = MemoryLink::default();
    let mut initiator = Peer::new(true, Role::Initiator, hello([1; 12], *b"zrtr-init-demo  "));
    let mut responder = Peer::new(false, Role::Responder, hello([2; 12], *b"zrtr-rsp-demo   "));

    for event in initiator.engine.start() {
        if let EngineEvent::Send(message) = event {
            initiator.emit(message, &mut link);
        }
    }
    for event in responder.engine.start() {
        if let EngineEvent::Send(message) = event {
            responder.emit(message, &mut link);
        }
    }

    while let Some((to_b, packet)) = link.pop() {
        if to_b {
            responder.handle_packet(packet, &mut link);
        } else {
            initiator.handle_packet(packet, &mut link);
        }
    }

    println!("initiator SAS: {:?}", initiator.engine.last_sas());
    println!("responder SAS: {:?}", responder.engine.last_sas());
    println!("initiator state: {:?}", initiator.engine.state());
    println!("responder state: {:?}", responder.engine.state());
}