Skip to content

Instantly share code, notes, and snippets.

@cite-reader
Created September 21, 2019 19:29
Show Gist options
  • Save cite-reader/6d9b41ef541a69ab2046610f889c5a37 to your computer and use it in GitHub Desktop.
Save cite-reader/6d9b41ef541a69ab2046610f889c5a37 to your computer and use it in GitHub Desktop.
Example code for authenticating CURVE peers
[package]
name = "curve-authenticator"
version = "0.1.0"
edition = "2018"
[dependencies]
env_logger = "0.6"
log = "0.4"
zmq = "0.9.2"
use std::collections::HashMap;
use std::ops::Deref;
use std::thread;
use env_logger::Env;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
fn main() {
env_logger::from_env(Env::default().default_filter_or("info")).init();
let users = vec![
("]7m>Wqh{db/5i0W)-CxjY3lXFKf^EPGd>HNX/0bv", "admin"),
(">TJrV[%HOVXw*L=E%}ZIoERs9&Q#juonEcbCyG-{", "karl"),
("B)[=t@Ca9P-xnB}}*gW.LNeghD{T)o>omO.@mEMu", "goose"),
]
.into_iter()
.map(|(key, id)| (zmq::z85_decode(key).unwrap(), id.into()))
.collect();
let ctx = zmq::Context::new();
let zap = ctx.socket(zmq::ROUTER).unwrap();
zap.set_linger(0).unwrap();
zap.bind("inproc://zeromq.zap.01").unwrap();
thread::spawn(move || authenticator(zap, users));
let server = ctx.socket(zmq::REP).unwrap();
server.set_zap_domain("example").unwrap();
server.set_curve_server(true).unwrap();
server
.set_curve_secretkey(b"[L>!FQkkhludw=47V]a!-yizer?otTeQ#HddzZKJ")
.unwrap();
server.set_linger(0).unwrap();
server.bind("tcp://127.0.0.1:*").unwrap();
let server_ep = server.get_last_endpoint().unwrap().unwrap();
thread::spawn(move || echo(server));
let client = ctx.socket(zmq::REQ).unwrap();
client
.set_curve_secretkey(b"PenRS7K+38d6jMw=zI#x%j%vsdY!Z])+x1.kz8]T")
.unwrap();
client
.set_curve_publickey(b"]7m>Wqh{db/5i0W)-CxjY3lXFKf^EPGd>HNX/0bv")
.unwrap();
client
.set_curve_serverkey(b"jQv(P^z%Y3%fh)yMxMvf!yMDJ^HmY.@IaeszH@&l")
.unwrap();
client.set_linger(0).unwrap();
client.connect(&server_ep).unwrap();
client.send("PING", 0).unwrap();
client.recv_msg(0).unwrap();
}
fn authenticator(s: zmq::Socket, known_keys: HashMap<Vec<u8>, String>) -> zmq::Result<Void> {
loop {
auth_one(&s, &known_keys)?;
}
}
macro_rules! zap_part {
($socket:expr) => {{
let part = $socket.recv_msg(0)?;
if !part.get_more() {
error!("Buggy ZAP server did not send a complete message");
return Ok(());
}
part
}};
}
fn auth_one(s: &zmq::Socket, known_keys: &HashMap<Vec<u8>, String>) -> zmq::Result<()> {
let mut have_envelope = false;
let mut envelope = vec![];
while !have_envelope {
let part = s.recv_msg(0)?;
if !part.get_more() {
error!("Buggy ZAP server did not send any message content");
return Ok(());
}
if &*part == b"" {
have_envelope = true;
}
envelope.push(part);
}
debug!("starting a ZAP request");
let version = zap_part!(s);
let request_id = zap_part!(s);
let _domain = zap_part!(s);
let _address = zap_part!(s);
let _identity = zap_part!(s);
let mechanism = s.recv_msg(0)?;
let mut have_all_creds = !mechanism.get_more();
let mut credentials = vec![];
while !have_all_creds {
let cred_part = s.recv_msg(0)?;
have_all_creds = !cred_part.get_more();
credentials.push(cred_part);
}
if &*version != b"1.0" {
warn!("A ZAP server requested a ZAP version not 1.0");
return zap_response(
s,
envelope,
request_id,
500,
"ZAP handler doesn't recognize that ZAP version",
"",
);
}
if &*mechanism != b"CURVE" || credentials.len() != 1 {
error!("This is demo code that only handles simple CURVE auth");
return zap_response(
s,
envelope,
request_id,
500,
"Don't want to handle that mechanism",
"",
);
}
let peer_key = &credentials[0];
if (&*peer_key).len() != 32 {
error!("Buggy ZAP server gave us a key of the wrong length");
return zap_response(s, envelope, request_id, 500, "wrong key length", "");
}
if let Some(user_id) = known_keys.get(peer_key.deref()) {
info!("Authenticated user {:?}", user_id);
return zap_response(s, envelope, request_id, 200, "OK", user_id);
} else {
info!("Unauthorized user");
return zap_response(s, envelope, request_id, 400, "Unknown Key", "");
}
}
fn zap_response(
s: &zmq::Socket,
envelope: Vec<zmq::Message>,
request_id: zmq::Message,
status: i32,
status_text: &str,
user_id: &str,
) -> zmq::Result<()> {
for frame in envelope {
s.send(frame, zmq::SNDMORE)?;
}
s.send("1.0", zmq::SNDMORE)?;
s.send(request_id, zmq::SNDMORE)?;
s.send(&format!("{}", status), zmq::SNDMORE)?;
s.send(status_text, zmq::SNDMORE)?;
s.send(user_id, zmq::SNDMORE)?;
s.send("", 0)?;
Ok(())
}
fn echo(s: zmq::Socket) -> zmq::Result<Void> {
loop {
let msg = s.recv_msg(0)?;
debug!("message {:?}", &*msg);
s.send(msg, 0)?;
}
}
enum Void {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment