f3cdd7bf21
* ropsten #6631425 foundation #8798209 (#11201) * [stable] builtin, istanbul and mordor testnet backports (#11234) * ethcore-builtin (#10850) * [builtin]: support `multiple prices and activations` in chain spec (#11039) * [chain specs]: activate `Istanbul` on mainnet (#11228) * ethcore/res: add mordor testnet configuration (#11200) * Update list of bootnodes for xDai chain (#11236) * ethcore: remove `test-helper feat` from build (#11047) * Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) * [stable]: backport #10691 and #10683 (#11143) * Fix compiler warning (that will become an error) (#10683) * Refactor Clique stepping (#10691) * Add Constantinople eips to the dev (instant_seal) config (#10809) * Add cargo-remote dir to .gitignore (?) * Insert explicit warning into the panic hook (#11225) * Fix docker centos build (#11226) * Update MIX bootnodes. (#11203) * Use provided usd-per-eth value if an endpoint is specified (#11209) * Add new line after writing block to hex file. (#10984) * Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) * Upgrade jsonrpc to latest (#11206) * [CI] check evmbin build (#11096) * Correct EIP-712 encoding (#11092) * [client]: Fix for incorrectly dropped consensus messages (#11086) * Fix block detail updating (#11015) * Switching sccache from local to Redis (#10971) * Made ecrecover implementation trait public (#11188) * [dependencies]: jsonrpc `14.0.1` (#11183) * [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) * [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) * util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) * ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) * Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) * Cleanup stratum a bit (#11161) * Upgrade to jsonrpc v14 (#11151) * SecretStore: expose restore_key_public in HTTP API (#10241)
524 lines
28 KiB
Rust
524 lines
28 KiB
Rust
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity Ethereum.
|
|
|
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity Ethereum is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use std::collections::BTreeSet;
|
|
use std::sync::{Arc, Weak};
|
|
use hyper::{self, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod,
|
|
StatusCode as HttpStatusCode, Body,
|
|
header::{self, HeaderValue},
|
|
server::conn::Http,
|
|
service::Service,
|
|
};
|
|
use serde::Serialize;
|
|
use serde_json;
|
|
use tokio;
|
|
use tokio::net::TcpListener;
|
|
use parity_runtime::Executor;
|
|
use futures::{future, Future, Stream};
|
|
use percent_encoding::percent_decode;
|
|
|
|
use traits::KeyServer;
|
|
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
|
|
use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId,
|
|
EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId};
|
|
|
|
/// Key server http-requests listener. Available requests:
|
|
/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold}
|
|
/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key}
|
|
/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold}
|
|
/// To get public portion of server key: GET /server/{server_key_id}/{signature}
|
|
/// To get document key: GET /{server_key_id}/{signature}
|
|
/// To get document key shadow: GET /shadow/{server_key_id}/{signature}
|
|
/// To generate Schnorr signature with server key: GET /schnorr/{server_key_id}/{signature}/{message_hash}
|
|
/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash}
|
|
/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids
|
|
|
|
pub struct KeyServerHttpListener {
|
|
_executor: Executor,
|
|
_handler: Arc<KeyServerSharedHttpHandler>,
|
|
}
|
|
|
|
/// Parsed http request
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
enum Request {
|
|
/// Invalid request
|
|
Invalid,
|
|
/// Generate server key.
|
|
GenerateServerKey(ServerKeyId, RequestSignature, usize),
|
|
/// Store document key.
|
|
StoreDocumentKey(ServerKeyId, RequestSignature, Public, Public),
|
|
/// Generate encryption key.
|
|
GenerateDocumentKey(ServerKeyId, RequestSignature, usize),
|
|
/// Request public portion of server key.
|
|
GetServerKey(ServerKeyId, RequestSignature),
|
|
/// Request encryption key of given document for given requestor.
|
|
GetDocumentKey(ServerKeyId, RequestSignature),
|
|
/// Request shadow of encryption key of given document for given requestor.
|
|
GetDocumentKeyShadow(ServerKeyId, RequestSignature),
|
|
/// Generate Schnorr signature for the message.
|
|
SchnorrSignMessage(ServerKeyId, RequestSignature, MessageHash),
|
|
/// Generate ECDSA signature for the message.
|
|
EcdsaSignMessage(ServerKeyId, RequestSignature, MessageHash),
|
|
/// Change servers set.
|
|
ChangeServersSet(RequestSignature, RequestSignature, BTreeSet<NodeId>),
|
|
}
|
|
|
|
/// Cloneable http handler
|
|
#[derive(Clone)]
|
|
struct KeyServerHttpHandler {
|
|
handler: Arc<KeyServerSharedHttpHandler>,
|
|
}
|
|
|
|
/// Shared http handler
|
|
struct KeyServerSharedHttpHandler {
|
|
key_server: Weak<KeyServer>,
|
|
}
|
|
|
|
impl KeyServerHttpListener {
|
|
/// Start KeyServer http listener
|
|
pub fn start(listener_address: NodeAddress, key_server: Weak<KeyServer>, executor: Executor) -> Result<Self, Error> {
|
|
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
|
|
key_server: key_server,
|
|
});
|
|
|
|
let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?;
|
|
let listener = TcpListener::bind(&listener_address)?;
|
|
|
|
let shared_handler2 = shared_handler.clone();
|
|
|
|
let server = listener.incoming()
|
|
.map_err(|e| warn!("Key server listener error: {:?}", e))
|
|
.for_each(move |socket| {
|
|
let http = Http::new();
|
|
let serve = http.serve_connection(socket,
|
|
KeyServerHttpHandler { handler: shared_handler2.clone() }
|
|
).map(|_| ()).map_err(|e| {
|
|
warn!("Key server handler error: {:?}", e);
|
|
});
|
|
|
|
tokio::spawn(serve)
|
|
});
|
|
|
|
executor.spawn(server);
|
|
|
|
let listener = KeyServerHttpListener {
|
|
_executor: executor,
|
|
_handler: shared_handler,
|
|
};
|
|
|
|
Ok(listener)
|
|
}
|
|
}
|
|
|
|
impl KeyServerHttpHandler {
|
|
fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse<Body> {
|
|
match parse_request(&req_method, &path, &req_body) {
|
|
Request::GenerateServerKey(document, signature, threshold) => {
|
|
return_server_public_key(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.generate_key(&document, &signature.into(), threshold))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "GenerateServerKey request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => {
|
|
return_empty(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.store_document_key(&document, &signature.into(), common_point, encrypted_document_key))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "StoreDocumentKey request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::GenerateDocumentKey(document, signature, threshold) => {
|
|
return_document_key(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.generate_document_key(&document, &signature.into(), threshold))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "GenerateDocumentKey request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::GetServerKey(document, signature) => {
|
|
return_server_public_key(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.restore_key_public(&document, &signature.into()))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "GetServerKey request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::GetDocumentKey(document, signature) => {
|
|
return_document_key(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.restore_document_key(&document, &signature.into()))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "GetDocumentKey request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::GetDocumentKeyShadow(document, signature) => {
|
|
return_document_key_shadow(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.restore_document_key_shadow(&document, &signature.into()))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "GetDocumentKeyShadow request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::SchnorrSignMessage(document, signature, message_hash) => {
|
|
return_message_signature(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.sign_message_schnorr(&document, &signature.into(), message_hash))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "SchnorrSignMessage request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::EcdsaSignMessage(document, signature, message_hash) => {
|
|
return_message_signature(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.sign_message_ecdsa(&document, &signature.into(), message_hash))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "EcdsaSignMessage request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => {
|
|
return_empty(&req_uri, self.handler.key_server.upgrade()
|
|
.map(|key_server| key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set))
|
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
|
.map_err(|err| {
|
|
warn!(target: "secretstore", "ChangeServersSet request {} has failed with: {}", req_uri, err);
|
|
err
|
|
}))
|
|
},
|
|
Request::Invalid => {
|
|
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
|
|
HttpResponse::builder()
|
|
.status(HttpStatusCode::BAD_REQUEST)
|
|
.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Service for KeyServerHttpHandler {
|
|
type ReqBody = Body;
|
|
type ResBody = Body;
|
|
type Error = hyper::Error;
|
|
type Future = Box<Future<Item = HttpResponse<Self::ResBody>, Error=Self::Error> + Send>;
|
|
|
|
fn call(&mut self, req: HttpRequest<Body>) -> Self::Future {
|
|
if req.headers().contains_key(header::ORIGIN) {
|
|
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri());
|
|
return Box::new(future::ok(HttpResponse::builder()
|
|
.status(HttpStatusCode::NOT_FOUND)
|
|
.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")))
|
|
}
|
|
|
|
let req_method = req.method().clone();
|
|
let req_uri = req.uri().clone();
|
|
// We cannot consume Self because of the Service trait requirement.
|
|
let this = self.clone();
|
|
|
|
Box::new(req.into_body().concat2().map(move |body| {
|
|
let path = req_uri.path().to_string();
|
|
if path.starts_with("/") {
|
|
this.process(req_method, req_uri, &path, &body)
|
|
} else {
|
|
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
|
|
HttpResponse::builder()
|
|
.status(HttpStatusCode::NOT_FOUND)
|
|
.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")
|
|
}
|
|
}))
|
|
}
|
|
}
|
|
|
|
fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse<Body> {
|
|
return_bytes::<i32>(req_uri, empty.map(|_| None))
|
|
}
|
|
|
|
fn return_server_public_key(req_uri: &Uri, server_public: Result<Public, Error>) -> HttpResponse<Body> {
|
|
return_bytes(req_uri, server_public.map(|k| Some(SerializablePublic(k))))
|
|
}
|
|
|
|
fn return_message_signature(req_uri: &Uri, signature: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
|
return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s))))
|
|
}
|
|
|
|
fn return_document_key(req_uri: &Uri, document_key: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
|
return_bytes(req_uri, document_key.map(|k| Some(SerializableBytes(k))))
|
|
}
|
|
|
|
fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result<EncryptedDocumentKeyShadow, Error>)
|
|
-> HttpResponse<Body>
|
|
{
|
|
return_bytes(req_uri, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow {
|
|
decrypted_secret: k.decrypted_secret.into(),
|
|
common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(),
|
|
decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(),
|
|
})))
|
|
}
|
|
|
|
fn return_bytes<T: Serialize>(req_uri: &Uri, result: Result<Option<T>, Error>) -> HttpResponse<Body> {
|
|
match result {
|
|
Ok(Some(result)) => match serde_json::to_vec(&result) {
|
|
Ok(result) => {
|
|
let body: Body = result.into();
|
|
HttpResponse::builder()
|
|
.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"))
|
|
.body(body)
|
|
.expect("Error creating http response")
|
|
},
|
|
Err(err) => {
|
|
warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err);
|
|
HttpResponse::builder()
|
|
.status(HttpStatusCode::INTERNAL_SERVER_ERROR)
|
|
.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")
|
|
}
|
|
},
|
|
Ok(None) => {
|
|
HttpResponse::builder()
|
|
.status(HttpStatusCode::OK)
|
|
.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")
|
|
},
|
|
Err(err) => return_error(err),
|
|
}
|
|
}
|
|
|
|
fn return_error(err: Error) -> HttpResponse<Body> {
|
|
let status = match err {
|
|
| Error::AccessDenied
|
|
| Error::ConsensusUnreachable
|
|
| Error::ConsensusTemporaryUnreachable =>
|
|
HttpStatusCode::FORBIDDEN,
|
|
| Error::ServerKeyIsNotFound
|
|
| Error::DocumentKeyIsNotFound =>
|
|
HttpStatusCode::NOT_FOUND,
|
|
| Error::InsufficientRequesterData(_)
|
|
| Error::Hyper(_)
|
|
| Error::Serde(_)
|
|
| Error::DocumentKeyAlreadyStored
|
|
| Error::ServerKeyAlreadyGenerated =>
|
|
HttpStatusCode::BAD_REQUEST,
|
|
_ => HttpStatusCode::INTERNAL_SERVER_ERROR,
|
|
};
|
|
|
|
let mut res = HttpResponse::builder();
|
|
res.status(status);
|
|
|
|
// return error text. ignore errors when returning error
|
|
let error_text = format!("\"{}\"", err);
|
|
if let Ok(error_text) = serde_json::to_vec(&error_text) {
|
|
res.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"));
|
|
res.body(error_text.into())
|
|
.expect("`error_text` is a formatted string, parsing cannot fail; qed")
|
|
} else {
|
|
res.body(Body::empty())
|
|
.expect("Nothing to parse, cannot fail; qed")
|
|
}
|
|
}
|
|
|
|
fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request {
|
|
let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() {
|
|
Ok(path) => path,
|
|
Err(_) => return Request::Invalid,
|
|
};
|
|
|
|
let path: Vec<String> = uri_path.trim_start_matches('/').split('/').map(Into::into).collect();
|
|
if path.len() == 0 {
|
|
return Request::Invalid;
|
|
}
|
|
|
|
if path[0] == "admin" {
|
|
return parse_admin_request(method, path, body);
|
|
}
|
|
|
|
let is_known_prefix = &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" || &path[0] == "server";
|
|
let (prefix, args_offset) = if is_known_prefix { (&*path[0], 1) } else { ("", 0) };
|
|
let args_count = path.len() - args_offset;
|
|
if args_count < 2 || path[args_offset].is_empty() || path[args_offset + 1].is_empty() {
|
|
return Request::Invalid;
|
|
}
|
|
|
|
let document = match path[args_offset].parse() {
|
|
Ok(document) => document,
|
|
_ => return Request::Invalid,
|
|
};
|
|
let signature = match path[args_offset + 1].parse() {
|
|
Ok(signature) => signature,
|
|
_ => return Request::Invalid,
|
|
};
|
|
|
|
let threshold = path.get(args_offset + 2).map(|v| v.parse());
|
|
let message_hash = path.get(args_offset + 2).map(|v| v.parse());
|
|
let common_point = path.get(args_offset + 2).map(|v| v.parse());
|
|
let encrypted_key = path.get(args_offset + 3).map(|v| v.parse());
|
|
match (prefix, args_count, method, threshold, message_hash, common_point, encrypted_key) {
|
|
("shadow", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) =>
|
|
Request::GenerateServerKey(document, signature, threshold),
|
|
("shadow", 4, &HttpMethod::POST, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) =>
|
|
Request::StoreDocumentKey(document, signature, common_point, encrypted_key),
|
|
("", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) =>
|
|
Request::GenerateDocumentKey(document, signature, threshold),
|
|
("server", 2, &HttpMethod::GET, _, _, _, _) =>
|
|
Request::GetServerKey(document, signature),
|
|
("", 2, &HttpMethod::GET, _, _, _, _) =>
|
|
Request::GetDocumentKey(document, signature),
|
|
("shadow", 2, &HttpMethod::GET, _, _, _, _) =>
|
|
Request::GetDocumentKeyShadow(document, signature),
|
|
("schnorr", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) =>
|
|
Request::SchnorrSignMessage(document, signature, message_hash),
|
|
("ecdsa", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) =>
|
|
Request::EcdsaSignMessage(document, signature, message_hash),
|
|
_ => Request::Invalid,
|
|
}
|
|
}
|
|
|
|
fn parse_admin_request(method: &HttpMethod, path: Vec<String>, body: &[u8]) -> Request {
|
|
let args_count = path.len();
|
|
if *method != HttpMethod::POST || args_count != 4 || path[1] != "servers_set_change" {
|
|
return Request::Invalid;
|
|
}
|
|
|
|
let old_set_signature = match path[2].parse() {
|
|
Ok(signature) => signature,
|
|
_ => return Request::Invalid,
|
|
};
|
|
|
|
let new_set_signature = match path[3].parse() {
|
|
Ok(signature) => signature,
|
|
_ => return Request::Invalid,
|
|
};
|
|
|
|
let new_servers_set: BTreeSet<SerializablePublic> = match serde_json::from_slice(body) {
|
|
Ok(new_servers_set) => new_servers_set,
|
|
_ => return Request::Invalid,
|
|
};
|
|
|
|
Request::ChangeServersSet(old_set_signature, new_set_signature,
|
|
new_servers_set.into_iter().map(Into::into).collect())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::sync::Arc;
|
|
use std::str::FromStr;
|
|
|
|
use hyper::Method as HttpMethod;
|
|
use ethkey::Public;
|
|
use ethereum_types::H256;
|
|
use traits::KeyServer;
|
|
use key_server::tests::DummyKeyServer;
|
|
use types::NodeAddress;
|
|
use parity_runtime::Runtime;
|
|
use super::{parse_request, Request, KeyServerHttpListener};
|
|
|
|
#[test]
|
|
fn http_listener_successfully_drops() {
|
|
let key_server: Arc<KeyServer> = Arc::new(DummyKeyServer::default());
|
|
let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 };
|
|
let runtime = Runtime::with_thread_count(1);
|
|
let listener = KeyServerHttpListener::start(address, Arc::downgrade(&key_server),
|
|
runtime.executor()).unwrap();
|
|
drop(listener);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_request_successful() {
|
|
// POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
|
|
Request::GenerateServerKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
2));
|
|
// POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} => store encrypted document key
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()),
|
|
Request::StoreDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
"b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(),
|
|
"1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap()));
|
|
// POST /{server_key_id}/{signature}/{threshold} => generate server && document key
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
|
|
Request::GenerateDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
2));
|
|
// GET /server/{server_key_id}/{signature} => get public portion of server key
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/server/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
|
|
Request::GetServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
|
|
// GET /{server_key_id}/{signature} => get document key
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
|
|
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
|
|
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
|
|
// GET /shadow/{server_key_id}/{signature} => get document key shadow
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
|
|
Request::GetDocumentKeyShadow("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
|
|
// GET /schnorr/{server_key_id}/{signature}/{message_hash} => schnorr-sign message with server key
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
|
|
Request::SchnorrSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
"281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()));
|
|
// GET /ecdsa/{server_key_id}/{signature}/{message_hash} => ecdsa-sign message with server key
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
|
|
Request::EcdsaSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
"281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()));
|
|
// POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body
|
|
let node1: Public = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap();
|
|
let node2: Public = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap();
|
|
let nodes = vec![node1, node2].into_iter().collect();
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01",
|
|
&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
|
|
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()),
|
|
Request::ChangeServersSet(
|
|
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
"b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
|
|
nodes,
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_request_failed() {
|
|
assert_eq!(parse_request(&HttpMethod::GET, "", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/shadow", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "///2", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/shadow///2", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/a/b", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/xxx/yyy",
|
|
&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
|
|
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()),
|
|
Request::Invalid);
|
|
assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()),
|
|
Request::Invalid);
|
|
}
|
|
}
|