extern crate pcsc;
#[macro_use]
extern crate simple_error;
+#[macro_use]
+extern crate serde_json;
use pcsc::*;
use core::task::{Context, Poll};
-use futures_util::{
- future::TryFutureExt,
- stream::{Stream, StreamExt, TryStreamExt},
-};
+use futures_util::stream::{Stream, StreamExt};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper::header::HeaderValue;
// Create a TCP listener via tokio.
let mut tcp = TcpListener::bind(&addr).await?;
- let tls_acceptor = TlsAcceptor::from(tls_cfg);
+ let tls_acceptor = &TlsAcceptor::from(tls_cfg);
// Prepare a long-running future stream to accept and serve cients.
let incoming_tls_stream = tcp
.incoming()
- .map_err(|e| error(format!("Incoming failed: {:?}", e)))
- .and_then(move |s| {
- tls_acceptor.accept(s).map_err(|e| {
- println!("[!] Voluntary server halt due to client-connection error...");
- // Errors could be handled here, instead of server aborting.
- // println!("TLS Error: {:?}", e);
- error(format!("TLS Error: {:?}", e))
- })
+ .filter_map(move |s| async move {
+ let client = match s {
+ Ok(x) => x,
+ Err(e) => {
+ println!("Failed to accept a client, should probably back off");
+ return Some(Err(e));
+ }
+ };
+ match tls_acceptor.accept(client).await {
+ Ok(x) => Some(Ok(x)),
+ Err(e) => {
+ println!("[!] Client connection error: {}", e);
+ None
+ }
+ }
})
.boxed();
*response.status_mut() = StatusCode::BAD_REQUEST;
}
(&Method::POST, "/scard/version/") => {
- *response.body_mut() = Body::from("{\"version\":\"1.3.9.46\"}");
+ let reply = json!({
+ "version": "1.4.1.16"
+ });
+ *response.body_mut() = Body::from(reply.to_string());
}
(&Method::POST, "/scard/list/") => {
return unwrap_result_into_response(get_readers(), response);
}
(&Method::POST, "/scard/disconnect/") => {
- *response.body_mut() = Body::from("{\"errorcode\":0,\"errordetail\":0,\"apduresponses\":null}");
+ *response.body_mut() = Body::from("");
println!("Disconnecting from card.");
let mut card = GLOBAL_CARD.lock().unwrap();
*card = None;
}
(&Method::POST, "/scard/getref/") => {
- let mut reply = "{\"data\":\"".to_owned();
-
// This is seemingly so that the PIN isn't sent “in the clear”, over TLS, to localhost.
// We send a key (in the form of 16 bytes) with an ID (the reference),
// the first four bytes are XORed with the next four bytes, and the PIN
// be a noop. (The reference is a handle to the key, in case we've given
// out multiple ones. We set it to some dummy value.)
let random_bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
- reply += &hex::encode(random_bytes).to_ascii_uppercase();
- reply += "\",\"ref\":195948557}";
- println!("Reply: {}", reply);
- *response.body_mut() = Body::from(reply);
+ let reply = json!({
+ "data": &hex::encode(random_bytes).to_ascii_uppercase(),
+ "ref": 195948557
+ });
+ println!("Reply: {}", reply.to_string());
+ *response.body_mut() = Body::from(reply.to_string());
}
// Catch-all 404.
_ => {
// Establish a PC/SC context.
let ctx = pcsc::Context::establish(Scope::User)?;
- let mut reply : String = "{\"errorcode\":0,\"errordetail\":0,\"readers\":[".to_owned();
-
// List available readers.
let mut readers_buf = [0; 2048];
- let mut reader_json : Vec<String> = Vec::new();
+ let mut reader_json : Vec<serde_json::Value> = Vec::new();
for r in ctx.list_readers(&mut readers_buf)? {
// Check the card status by connecting to it.
let status = match ctx.connect(r, ShareMode::Shared, Protocols::ANY) {
- Ok(_) => "302",
- Err(_) => "303"
+ Ok(_) => 302,
+ Err(_) => 303
};
- let mut json = "{\"cardstatus\":".to_owned();
- json += status;
- json += ",\"name\":\"";
- json += r.to_str()?;
- json += "\"}";
- reader_json.push(json);
+ reader_json.push(json!({
+ "cardstatus": status,
+ "name": r.to_str()?
+ }));
}
- reply += &reader_json.join(",");
- reply += "]}";
- println!("Reply: {}", reply);
- return Ok(Body::from(reply));
+ let reply = json!({
+ "errorcode": 0,
+ "errordetail": 0,
+ "readers": reader_json
+ });
+ println!("Reply: {}", reply.to_string());
+ return Ok(Body::from(reply.to_string()));
}
fn transmit_apdu(card: &Card, mut apdu: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
- if apdu[0] == 0xff && apdu[1] == 0xff && apdu[2] == 0x01 && apdu[3] == 0x04 {
+ if apdu.len() >= 11 && apdu[0] == 0xff && apdu[1] == 0xff && apdu[2] == 0x01 && apdu[3] == 0x04 {
// APDUs with PIN codes are obfuscated (see /getref/ above)
// with a special extension header used only in scproxy. The format seems to be:
//
let apdus = get_apducommands(req.clone())?;
let mut any_sensitive = false;
for apdu in &apdus {
- if apdu[0] == 0xff && apdu[1] == 0xff {
+ if apdu.len() >= 2 && apdu[0] == 0xff && apdu[1] == 0xff {
any_sensitive = true;
}
}
};
}
- let mut reply : String = "{\"errorcode\":0,\"errordetail\":0,\"apduresponses\":[".to_owned();
-
- let mut response_json : Vec<String> = Vec::new();
+ let mut response_json : Vec<serde_json::Value> = Vec::new();
for apdu in apdus {
- let mut reply : String = "{\"apdu\":\"".to_owned();
- reply += &transmit_apdu(card.as_mut().unwrap(), &apdu)?;
- reply += "\"}";
- response_json.push(reply);
+ let reply = match transmit_apdu(card.as_mut().unwrap(), &apdu) {
+ Ok(r) => r,
+ Err(err) => {
+ *card = None; // Clean up.
+ return Err(err);
+ }
+ };
+ response_json.push(json!({
+ "apdu": reply
+ }));
}
-
- reply += &response_json.join(",");
- reply += "]}";
- println!("Reply: {}", reply);
- return Ok(Body::from(reply));
+ let reply = json!({
+ "errorcode": 0,
+ "errordetail": 0,
+ "apduresponses": response_json
+ });
+ println!("Reply: {}", reply.to_string());
+ return Ok(Body::from(reply.to_string()));
}