Skip to content
This repository has been archived by the owner on Jun 19, 2022. It is now read-only.

Port everything to asyncio #14

Merged
merged 89 commits into from
Sep 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
c25e572
bump everything on the planet
alex Aug 30, 2017
7103e74
bumpre more crap
alex Aug 30, 2017
f35f74f
general progress
alex Aug 30, 2017
ac52dcd
port this file
alex Aug 30, 2017
1c4bc70
port another module
alex Aug 30, 2017
8d5cb39
cargo fmt
alex Aug 30, 2017
e9d514e
pretty sure this must be a result
alex Aug 30, 2017
4b899b2
cargo fmt
alex Aug 30, 2017
c8fa478
I think these need to be results
alex Aug 30, 2017
fde638e
bring these back
alex Aug 30, 2017
b752849
progress
alex Aug 31, 2017
c2a55cf
type fixes
alex Aug 31, 2017
985f6c7
more progress
alex Aug 31, 2017
988325c
cargo fmt
alex Aug 31, 2017
77e5869
cargo update futures-await
alex Aug 31, 2017
74fec97
Try this...
alex Aug 31, 2017
7b8b8ba
progress...
alex Aug 31, 2017
38265b9
uptake ring/rustls and friends
alex Aug 31, 2017
a5d8225
attempted travis fixes
alex Aug 31, 2017
28d2dda
dep bump
alex Sep 1, 2017
29b7197
the lib portion now compiles!
alex Sep 1, 2017
2643b61
switch from Box to references with lifetimes
alex Sep 1, 2017
73982a9
remove more box
alex Sep 1, 2017
eeb5d28
cargo fmt
alex Sep 1, 2017
1acf0b3
forward progress on compiling
alex Sep 1, 2017
d41b570
more progress
alex Sep 1, 2017
d338616
more
alex Sep 1, 2017
b4a2bae
enough progress to get more errors
alex Sep 1, 2017
cee0b99
Port submit
alex Sep 1, 2017
0ef32ab
Progress
alex Sep 1, 2017
c2f8a06
Comment this out so that we can make progress on compilation
alex Sep 1, 2017
d910536
knock out a TODO
alex Sep 1, 2017
708abd9
this looks way saner
alex Sep 2, 2017
6901246
bump future-await
alex Sep 2, 2017
5896235
progress
alex Sep 2, 2017
d932ec5
oops, this needs a fucntion
alex Sep 2, 2017
8931944
format this differently
alex Sep 2, 2017
c56648a
more progress
alex Sep 2, 2017
c2832b1
fixes
alex Sep 2, 2017
3973462
TODO
alex Sep 2, 2017
d0d12b2
fix a warning with this silliness
alex Sep 2, 2017
96cd582
this isnt for anything
alex Sep 2, 2017
339b6dc
fix
alex Sep 2, 2017
08338af
less code
alex Sep 2, 2017
7366d6a
reorganize code
alex Sep 2, 2017
12c4f47
move this around
alex Sep 2, 2017
69fe7c4
cargo update
alex Sep 3, 2017
cdb6c08
cargo fmt
alex Sep 3, 2017
fac4694
cargo update
alex Sep 4, 2017
496195a
cargo update
alex Sep 5, 2017
cb6567e
cargo update
alex Sep 6, 2017
6e34d9a
some comments, and a cleanup
alex Sep 7, 2017
e43ad26
fix
alex Sep 7, 2017
aabe711
some progress on these errors
alex Sep 8, 2017
7ed8d73
one left!
alex Sep 8, 2017
353b66d
IT COMPILES!
alex Sep 8, 2017
c111d4f
cargo fmt
alex Sep 8, 2017
c09007d
fix submission
alex Sep 8, 2017
f2ebe6e
style
alex Sep 8, 2017
61b6a77
port the whole check to be stream based
alex Sep 9, 2017
f7e7600
Parallelism!
alex Sep 9, 2017
8414c56
there's a pool by default
alex Sep 9, 2017
5a81ef3
bump serde
alex Sep 9, 2017
8da5cd7
bumped futures-await
alex Sep 11, 2017
b545b85
cargo update gcc
Sep 12, 2017
0cff7ae
cargo update
Sep 14, 2017
bcfab40
cargo update futures
Sep 15, 2017
d0aec9c
cargo update
alex Sep 17, 2017
f305cc6
cargo update serde
alex Sep 17, 2017
79a6481
cargo update
Sep 19, 2017
2705a59
cargo update libc
alex Sep 20, 2017
5ddfc0e
cargo update
alex Sep 20, 2017
ef96cdb
cargo update
alex Sep 20, 2017
5d0f0ca
This got fixed apparently
alex Sep 21, 2017
e63d818
clippy fixes (requires the very latest nightly)
alex Sep 22, 2017
0ee3e9a
more clippy fixes!
alex Sep 22, 2017
c5cec0f
Do what clippy tells me
alex Sep 22, 2017
fdcc770
Do what clippy tells me
alex Sep 22, 2017
1308a8c
more aggressive fmting
alex Sep 22, 2017
3be5041
Fixed #15 -- added a --all-logs instead of --log-urls
alex Sep 22, 2017
d84d9d3
Added a timeout to submits
alex Sep 22, 2017
e3a730f
obey clippy
alex Sep 22, 2017
56bdd52
Drop tokio-proto and do everything myself... seems to work
alex Sep 22, 2017
398220d
Don't double borrow
alex Sep 22, 2017
4893482
cargo update
alex Sep 23, 2017
51665a6
Simplify
alex Sep 23, 2017
c6b0c1e
Simplify!
alex Sep 23, 2017
a4e8305
Order things in check, and don't print the sct in submit
alex Sep 23, 2017
576de09
Fixed formatting certs
alex Sep 23, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
sudo: false
dist: trusty
language: rust
cache: cargo
rust:
- stable
- nightly
before_script:
- export PATH=$HOME/.cargo/bin:$PATH
Expand Down
634 changes: 511 additions & 123 deletions Cargo.lock

Large diffs are not rendered by default.

19 changes: 13 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ version = "0.1.0"
authors = ["Alex Gaynor <[email protected]>"]

[dependencies]
futures = "*"
futures-await = { git = "https://github.com/alexcrichton/futures-await" }
tokio-core = "*"
tokio-io = "*"
tokio-process = "*"
tokio-rustls = "*"
tokio-service = "*"
net2 = "*"

acme-client = "*"

base64 = "*"
Expand All @@ -16,16 +25,14 @@ clap = "*"

hex = "*"

hyper = "< 0.11"
hyper-rustls = ">=0.6"
hyper = ">=0.11"
hyper-rustls = ">=0.11"

openssl = "*"

rayon = "*"

ring = "*"
ring = ">=0.12"

rustls = { version = ">=0.8", features = ["dangerous_configuration"] }
rustls = { version = ">=0.11", features = ["dangerous_configuration"] }

serde = "*"
serde_derive = "*"
Expand Down
3 changes: 2 additions & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
reorder_imports = true
reorder_imported_names = true
reorder_imports_in_group = true
normalize_imports = true
normalize_comments = false
normalize_comments = true
87 changes: 56 additions & 31 deletions src/crtsh.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,73 @@
use super::common::sha256_hex;
use super::ct::AddChainRequest;
use base64;
use futures::prelude::*;
use hyper;
use serde_json;
use url;

pub fn build_chain_for_cert(http_client: &hyper::Client, cert: &[u8]) -> Option<Vec<Vec<u8>>> {
pub fn build_chain_for_cert<C: hyper::client::Connect>(
http_client: &hyper::Client<C>,
cert: &[u8],
) -> impl Future<Item = Vec<Vec<u8>>, Error = ()> {
let body = url::form_urlencoded::Serializer::new(String::new())
.append_pair("b64cert", &base64::encode(cert))
.append_pair("b64cert", &base64::encode(&cert))
.append_pair("onlyonechain", "Y")
.finish();
let body_bytes = body.as_bytes();
let response = match http_client
.post("https://crt.sh/gen-add-chain")
.header(hyper::header::ContentType::form_url_encoded())
.header(hyper::header::Connection::keep_alive())
.body(hyper::client::Body::BufBody(body_bytes, body_bytes.len()))
.send() {
Ok(response) => response,
// TODO: maybe be more selective in error handling
Err(_) => return None,
};
let mut request = hyper::Request::new(
hyper::Method::Post,
"https://crt.sh/gen-add-chain".parse().unwrap(),
);
request.headers_mut().set(
hyper::header::ContentType::form_url_encoded(),
);
request.headers_mut().set(
hyper::header::Connection::keep_alive(),
);
request.set_body(body.into_bytes());
// TODO: undo this once lifetime bugs are fixed
let r = http_client.request(request);
async_block! {
let response = match await!(r) {
Ok(response) => response,
// TODO: maybe be more selective in error handling
Err(_) => return Err(()),
};

if response.status == hyper::status::StatusCode::NotFound {
return None;
}
if response.status() == hyper::StatusCode::NotFound {
return Err(());
}

let add_chain_request: AddChainRequest = serde_json::from_reader(response).unwrap();
Some(
add_chain_request
.chain
.iter()
.map(|c| base64::decode(c).unwrap())
.collect(),
)
let body = await!(response.body().concat2()).unwrap();
let add_chain_request: AddChainRequest = serde_json::from_slice(&body).unwrap();
Ok(
add_chain_request
.chain
.iter()
.map(|c| base64::decode(c).unwrap())
.collect(),
)
}
}

pub fn is_cert_logged(http_client: &hyper::Client, cert: &[u8]) -> bool {
let response = http_client
.get(&format!("https://crt.sh/?d={}", sha256_hex(cert)))
.header(hyper::header::Connection::keep_alive())
.send()
.unwrap();
response.status == hyper::status::StatusCode::Ok
pub fn is_cert_logged<C: hyper::client::Connect>(
http_client: &hyper::Client<C>,
cert: &[u8],
) -> impl Future<Item = bool, Error = ()> {
let mut request = hyper::Request::new(
hyper::Method::Get,
format!("https://crt.sh/?d={}", sha256_hex(cert))
.parse()
.unwrap(),
);
request.headers_mut().set(
hyper::header::Connection::keep_alive(),
);
let r = http_client.request(request);
async_block! {
let response = await!(r).unwrap();
Ok(response.status() == hyper::StatusCode::Ok)
}
}

pub fn url_for_cert(cert: &[u8]) -> String {
Expand Down
98 changes: 58 additions & 40 deletions src/ct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ use super::common::Log;

use base64;
use byteorder::{BigEndian, WriteBytesExt};

use futures;
use futures::prelude::*;
use hyper;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use serde_json;
use std::io::{Read, Write};
use std::io::Write;
use std::time::Duration;
use tokio_core::reactor::Timeout;


#[derive(Debug, Deserialize)]
Expand All @@ -28,7 +32,7 @@ impl SignedCertificateTimestamp {
b.write_u64::<BigEndian>(self.timestamp).unwrap();

let extensions = base64::decode(&self.extensions).unwrap();
assert!(extensions.len() <= 65535);
assert!(extensions.len() <= 65_535);
b.write_u16::<BigEndian>(extensions.len() as u16).unwrap();
b.write_all(&extensions).unwrap();

Expand All @@ -40,59 +44,73 @@ impl SignedCertificateTimestamp {
}


fn submit_to_log(
http_client: &hyper::Client,
url: &str,
payload: &[u8],
) -> Option<SignedCertificateTimestamp> {
let mut url = "https://".to_string() + url;
fn submit_to_log<C: hyper::client::Connect>(
http_client: &hyper::Client<C>,
log: &Log,
payload: Vec<u8>,
) -> impl Future<Item = SignedCertificateTimestamp, Error = ()> {
let mut url = "https://".to_string() + &log.url;
if !url.ends_with('/') {
url += "/";
}
url += "ct/v1/add-chain";
let response = http_client
.post(&url)
.body(hyper::client::Body::BufBody(payload, payload.len()))
.header(hyper::header::ContentType::json())
.send();
let response = match response {
Ok(r) => r,
// TODO: maybe not all of these should be silently ignored.
Err(_) => return None,
};

// 400, 403, and probably some others generally indicate a log doesn't accept certs from this
// root, or that the log isn't accepting new submissions. Server errors mean there's nothing we
// can do.
if response.status.is_client_error() || response.status.is_server_error() {
return None;
let mut request = hyper::Request::new(hyper::Method::Post, url.parse().unwrap());
request.headers_mut().set(
hyper::header::ContentType::json(),
);
request.set_body(payload);
let r = http_client.request(request);
async_block! {
let response = match await!(r) {
Ok(r) => r,
// TODO: maybe not all of these should be silently ignored.
Err(_) => return Err(()),
};

// 400, 403, and probably some others generally indicate a log doesn't accept certs from
// this root, or that the log isn't accepting new submissions. Server errors mean there's
// nothing we can do.
if response.status().is_client_error() || response.status().is_server_error() {
return Err(());
}

// Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS
// in the face of a dumb or malicious log.
let body = await!(response.body().take(10 * 1024 * 1024).concat2())
.unwrap();
Ok(serde_json::from_slice(&body).unwrap())
}

// Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS in
// the face of a dumb or malicious log.
Some(
serde_json::from_reader(response.take(10 * 1024 * 1024)).unwrap(),
)
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
pub chain: Vec<String>,
}

pub fn submit_cert_to_logs<'a>(
http_client: &hyper::Client,
logs: &'a [Log],
pub fn submit_cert_to_logs<C: hyper::client::Connect>(
http_client: &hyper::Client<C>,
logs: &[Log],
cert: &[Vec<u8>],
) -> Vec<(&'a Log, SignedCertificateTimestamp)> {
) -> impl Future<Item = Vec<(usize, SignedCertificateTimestamp)>, Error = ()> {
let payload = serde_json::to_vec(&AddChainRequest {
chain: cert.iter().map(|r| base64::encode(r)).collect(),
}).unwrap();

logs.par_iter()
.filter_map(|log| {
let sct = submit_to_log(http_client, &log.url, &payload);
sct.map(|s| (log, s))
let futures = logs.iter()
.enumerate()
.map(move |(idx, log)| {
let timeout = Timeout::new(Duration::from_secs(5), http_client.handle()).unwrap();

let payload = payload.clone();
let s = submit_to_log(http_client, log, payload).select2(timeout);
async_block! {
match await!(s) {
Ok(futures::future::Either::A((sct, _))) => Ok(Some((idx, sct))),
_ => Ok(None),
}
}
})
.collect()
.collect::<Vec<_>>();

futures::future::join_all(futures).map(|scts| scts.into_iter().filter_map(|s| s).collect())
}
75 changes: 49 additions & 26 deletions src/google.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use super::common::Log;

use futures::prelude::*;

use hyper;
use serde_json;
use std::io::Read;


const LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/log_list.json";
const TRUSTED_LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/log_list.json";
const ALL_LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/all_logs_list.json";

#[derive(Deserialize)]
struct LogsResponseLogs {
description: String,
Expand All @@ -26,28 +29,48 @@ struct LogsResponse {
operators: Vec<LogsResponseOperators>,
}

pub fn fetch_trusted_ct_logs(http_client: &hyper::Client) -> Vec<Log> {
let response = http_client.get(LOG_LIST_URL).send().unwrap();
// Limit the response to 10MB at most, to be resillient to DoS.
let logs_response: LogsResponse = serde_json::from_reader(response.take(10 * 1024 * 1024))
.unwrap();

let google_id = logs_response
.operators
.iter()
.find(|o| o.name == "Google")
.map(|o| o.id);

logs_response
.logs
.into_iter()
.filter(|log| log.disqualified_at.is_none())
.map(|log| {
Log {
url: log.url,
description: log.description,
is_google: log.operated_by.contains(&google_id.unwrap()),
}
})
.collect()
pub fn fetch_trusted_ct_logs<'a, C: hyper::client::Connect>(
http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
fetch_log_list(http_client, TRUSTED_LOG_LIST_URL.parse().unwrap())
}

pub fn fetch_all_ct_logs<'a, C: hyper::client::Connect>(
http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
fetch_log_list(http_client, ALL_LOG_LIST_URL.parse().unwrap())
}

fn fetch_log_list<'a, C: hyper::client::Connect>(
http_client: &'a hyper::Client<C>,
uri: hyper::Uri,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
async_block! {
let response = await!(http_client.get(uri)).unwrap();
// Limit the response to 10MB at most, to be resillient to DoS.
let body = await!(response.body().take(10 * 1024 * 1024).concat2()).unwrap();
let logs_response: LogsResponse = serde_json::from_slice(&body).unwrap();

let google_id = logs_response
.operators
.iter()
.find(|o| o.name == "Google")
.map(|o| o.id)
.unwrap();

Ok(
logs_response
.logs
.into_iter()
.filter(|log| log.disqualified_at.is_none())
.map(move |log| {
Log {
url: log.url,
description: log.description,
is_google: log.operated_by.contains(&google_id),
}
})
.collect(),
)
}
}
Loading