finish porting dnapi
This commit is contained in:
parent
e37d768ead
commit
f301684c3a
3 changed files with 70 additions and 4 deletions
|
@ -5,6 +5,7 @@
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tfclient/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/tfclient/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/trifid-api/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/dnapi-rs/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
|
@ -6,10 +6,10 @@ use log::{debug, error};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use trifid_pki::cert::serialize_ed25519_public;
|
use trifid_pki::cert::serialize_ed25519_public;
|
||||||
use trifid_pki::ed25519_dalek::{Signer, SigningKey};
|
use trifid_pki::ed25519_dalek::{Signature, Signer, SigningKey, Verifier};
|
||||||
use crate::credentials::{Credentials, ed25519_public_keys_from_pem};
|
use crate::credentials::{Credentials, ed25519_public_keys_from_pem};
|
||||||
use crate::crypto::new_keys;
|
use crate::crypto::{new_keys, nonce};
|
||||||
use crate::message::{ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper};
|
use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper};
|
||||||
|
|
||||||
/// A type alias to abstract return types
|
/// A type alias to abstract return types
|
||||||
pub type NebulaConfig = Vec<u8>;
|
pub type NebulaConfig = Vec<u8>;
|
||||||
|
@ -100,6 +100,66 @@ impl Client {
|
||||||
Ok((r.config, dh_privkey_pem, creds, meta))
|
Ok((r.config, dh_privkey_pem, creds, meta))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a signed message to the DNClient API to learn if there is a new configuration available.
|
||||||
|
pub fn check_for_update(&self, creds: &Credentials) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let body = self.post_dnclient(CHECK_FOR_UPDATE, &[], &creds.host_id, creds.counter, &creds.ed_privkey)?;
|
||||||
|
|
||||||
|
let result: CheckForUpdateResponseWrapper = serde_json::from_slice(&body)?;
|
||||||
|
|
||||||
|
Ok(result.data.update_available)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a signed message to the DNClient API to fetch the new configuration update. During this call a new
|
||||||
|
/// DH X25519 keypair is generated for the new Nebula certificate as well as a new Ed25519 keypair for DNClient API
|
||||||
|
/// communication. On success it returns the new config, a Nebula private key PEM to be inserted into the config
|
||||||
|
/// and new DNClient API credentials
|
||||||
|
pub fn do_update(&self, creds: &Credentials) -> Result<(NebulaConfig, DHPrivateKeyPEM, Credentials), Box<dyn Error>> {
|
||||||
|
let (dh_pubkey_pem, dh_privkey_pem, ed_pubkey, ed_privkey) = new_keys();
|
||||||
|
|
||||||
|
let update_keys = DoUpdateRequest {
|
||||||
|
ed_pubkey_pem: serialize_ed25519_public(ed_pubkey.as_bytes()),
|
||||||
|
dh_pubkey_pem,
|
||||||
|
nonce: nonce().to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let update_keys_blob = serde_json::to_vec(&update_keys)?;
|
||||||
|
|
||||||
|
let resp = self.post_dnclient(DO_UPDATE, &update_keys_blob, &creds.host_id, creds.counter, &creds.ed_privkey)?;
|
||||||
|
|
||||||
|
let result_wrapper: SignedResponseWrapper = serde_json::from_slice(&resp)?;
|
||||||
|
|
||||||
|
let mut valid = false;
|
||||||
|
|
||||||
|
for ca_pubkey in creds.trusted_keys {
|
||||||
|
if ca_pubkey.verify(&result_wrapper.data.message, &Signature::from_slice(&result_wrapper.data.signature)?).is_ok() {
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return Err("Failed to verify signed API result".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: DoUpdateResponse = serde_json::from_slice(&result_wrapper.data.message)?;
|
||||||
|
|
||||||
|
if result.nonce != update_keys.nonce {
|
||||||
|
error!("nonce mismatch between request {:x?} and response {:x?}", result.nonce, update_keys.nonce);
|
||||||
|
return Err(format!("nonce mismatch between request and response").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let trusted_keys = ed25519_public_keys_from_pem(&result.trusted_keys)?;
|
||||||
|
|
||||||
|
let new_creds = Credentials {
|
||||||
|
host_id: creds.host_id.clone(),
|
||||||
|
ed_privkey,
|
||||||
|
counter: result.counter,
|
||||||
|
trusted_keys,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((result.config, dh_privkey_pem, new_creds))
|
||||||
|
}
|
||||||
|
|
||||||
/// Wraps and signs the given req_type and value, and then makes the API call.
|
/// Wraps and signs the given req_type and value, and then makes the API call.
|
||||||
/// On success, returns the response body.
|
/// On success, returns the response body.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -107,7 +167,7 @@ impl Client {
|
||||||
/// - serialization in any step fails
|
/// - serialization in any step fails
|
||||||
/// - if the server_url is invalid
|
/// - if the server_url is invalid
|
||||||
/// - if the request could not be sent
|
/// - if the request could not be sent
|
||||||
pub fn post_dnclient(&self, req_type: &str, value: &[u8], host_id: &str, counter: u32, ed_privkey: SigningKey) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn post_dnclient(&self, req_type: &str, value: &[u8], host_id: &str, counter: u32, ed_privkey: &SigningKey) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let encoded_msg = serde_json::to_string(&RequestWrapper {
|
let encoded_msg = serde_json::to_string(&RequestWrapper {
|
||||||
message_type: req_type.to_string(),
|
message_type: req_type.to_string(),
|
||||||
value: value.to_vec(),
|
value: value.to_vec(),
|
||||||
|
|
|
@ -6,6 +6,11 @@ use serde::{Serialize, Deserialize};
|
||||||
/// The version 1 `DNClient` API endpoint
|
/// The version 1 `DNClient` API endpoint
|
||||||
pub const ENDPOINT_V1: &str = "/v1/dnclient";
|
pub const ENDPOINT_V1: &str = "/v1/dnclient";
|
||||||
|
|
||||||
|
/// The CheckForUpdate message type
|
||||||
|
pub const CHECK_FOR_UPDATE: &str = "CheckForUpdate";
|
||||||
|
/// The DoUpdate message type
|
||||||
|
pub const DO_UPDATE: &str = "DoUpdate";
|
||||||
|
|
||||||
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
|
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
|
Loading…
Reference in a new issue